diff --git a/History.md b/History.md
index e8eee99985..b436392698 100644
--- a/History.md
+++ b/History.md
@@ -1,39 +1,65 @@
-## batch-plugins
-
-Backwards-incompatible change: static assets in package.js files must
-be explicitly declared with `{isAsset: true}`. See the batch-plugins merge
-message for more details.
-
-
## v.NEXT
+## in progress: v1.2
+
+There are quite a lot of changes in Meteor 1.2. See the
+[Wiki](https://github.com/meteor/meteor/wiki/Breaking-changes-in-Meteor-1.2) for
+a shorter list of breaking changes you should be aware of when upgrading.
+
+### Core Packages
+
+* `meteor-platform` has been deprecated in favor of the smaller `meteor-base`,
+ with apps listing their other dependencies explicitly. The v1.2 upgrader
+ will rewrite `meteor-platform` in existing apps. `meteor-base` puts fewer
+ symbols in the global namepsace, so it's no longer true that all apps
+ have symbols like `Random` and `EJSON` in the global namespace.
+
+* New packages: `ecmascript`, `es5-shim`, `ecmascript-collections`, `promise`,
+ `static-html`, `jshint`, `babel-compiler`
+
* No longer include the `json` package by default, which contains code for
`JSON.parse` and `JSON.stringify`. (The last browser to not support JSON
natively was Internet Explorer 7.)
-### Utilities
-
-* New `beforeSend` option to `HTTP.call` on the client allows you to directly
- access the `XMLHttpRequest` object and abort the call. #4419 #3243 #3266
-
-* Parse `application/javascript` and `application/x-javascript` HTTP replies as
- JSON too. #4595
-
+* `autoupdate` has been renamed `hot-code-push`
### Meteor Accounts
-* `loginWithPassword` now matches username or email in a case insensitive manner. If there are multiple users with a username or email only differing in case, a case sensitive match is required. #550
+* Login attempts are now rate-limited by default. This can be turned off
+ using `Accounts.removeDefaultRateLimit()`.
-* `loginWithGithub` now requests `user:email` scope by default, and attempts to fetch the user's emails. If no public email has been set, we use the primary email instead. We also store the complete list of emails. #4545
+* `loginWithPassword` now matches username or email in a case insensitive
+ manner. If there are multiple users with a username or email only differing
+ in case, a case sensitive match is required. #550
+
+* `loginWithGithub` now requests `user:email` scope by default, and attempts
+ to fetch the user's emails. If no public email has been set, we use the
+ primary email instead. We also store the complete list of emails. #4545
+
+* When an account's email address is verified, deactivate other verification
+ tokens. #4626
+
+* Fix bug where blank page is shown when an expired login token is
+ present. #4825
* Fix `OAuth1Binding.prototype.call` when making requests to Twitter
with a large parameter set.
+* Directions for setting up Google OAuth in accounts-ui have been updated to
+ match Google's new requirements.
-### DDP
+* Add `Accounts.oauth.unregisterService` method, and ensure that users can only
+ log in with currently registered services. #4014
-* `sub.ready()` should return true inside that subscription's `onReady`
- callback. #4614
+* The `accounts-base` now defines reusable `AccountsClient` and
+ `AccountsServer` constructors, so that users can create multiple
+ independent instances of the `Accounts` namespace. #4233
+
+* Create an index for `Meteor.users` on
+ `services.email.verificationTokens.token` (instead of
+ `emails.validationTokens.token`, which never was used for anything). #4482
+
+* Remove an IE7-specific workaround from accounts-ui. #4485
### Livequery
@@ -95,15 +121,17 @@ message for more details.
iOS Simulator. As a workaround, you can `meteor run ios-device` to open the
project in Xcode and watch the output there.
-## in progress: v.1.1.1
+### Templates/Blaze
-### Blaze
+* New syntax: Handlebars sub-expressions are now supported -- as in,
+ `{{helper (anotherHelper arg1 arg2)}}` -- as well as new block helper forms
+ `#each .. in ..` and `#let x=y`. See
+ https://github.com/meteor/meteor/tree/devel/packages/spacebars
-* Preparatory work for the yet-unreleased `react-template-helper`
- package -- don't let templates use {{> React}} with siblings since
- `React.render` assumes it's being rendered into an empty container
- element. (This lets us throw the error when compiling templates
- rather than when the app runs.)
+* Add a special case for the new `react-template-helper` package -- don't let
+ templates use {{> React}} with siblings since `React.render` assumes it's
+ being rendered into an empty container element. (This lets us throw the error
+ when compiling templates rather than when the app runs.)
* Improve parsing of `
+
+
+
+
+
+ );
+ }
+});
diff --git a/examples/simple-todos-react/LICENSE b/examples/simple-todos-react/LICENSE
new file mode 100644
index 0000000000..e41973a6e1
--- /dev/null
+++ b/examples/simple-todos-react/LICENSE
@@ -0,0 +1,22 @@
+========================================
+Meteor is licensed under the MIT License
+========================================
+
+Copyright (C) 2011--2015 Meteor Development Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/examples/simple-todos-react/README.md b/examples/simple-todos-react/README.md
new file mode 100644
index 0000000000..214dd3a445
--- /dev/null
+++ b/examples/simple-todos-react/README.md
@@ -0,0 +1,9 @@
+# Simple Todo List
+
+The Meteor Tutorial app.
+
+Use it to share a single todo list with your friends. The list updates on everyone's screen in real time, and you can make tasks private if you don't want others to see them.
+
+Learn how to build this app by following the [Meteor Tutorial](http://www.meteor.com/install).
+
+
\ No newline at end of file
diff --git a/examples/simple-todos-react/Task.jsx b/examples/simple-todos-react/Task.jsx
new file mode 100644
index 0000000000..37eadd38b8
--- /dev/null
+++ b/examples/simple-todos-react/Task.jsx
@@ -0,0 +1,52 @@
+// Task component - represents a single todo item
+Task = React.createClass({
+ propTypes: {
+ task: React.PropTypes.object.isRequired,
+ showPrivateButton: React.PropTypes.bool.isRequired
+ },
+
+ toggleChecked() {
+ // Set the checked property to the opposite of its current value
+ Meteor.call("setChecked", this.props.task._id, ! this.props.task.checked);
+ },
+
+ deleteThisTask() {
+ Meteor.call("removeTask", this.props.task._id);
+ },
+
+ togglePrivate() {
+ Meteor.call("setPrivate", this.props.task._id, ! this.props.task.private);
+ },
+
+ render() {
+ // Give tasks a different className when they are checked off,
+ // so that we can style them nicely in CSS
+ // Add "checked" and/or "private" to the className when needed
+ const taskClassName = (this.props.task.checked ? "checked" : "") + " " +
+ (this.props.task.private ? "private" : "");
+
+ return (
+
+
diff --git a/examples/simple-todos/simple-todos.js b/examples/simple-todos/simple-todos.js
new file mode 100644
index 0000000000..9bf41bbfc8
--- /dev/null
+++ b/examples/simple-todos/simple-todos.js
@@ -0,0 +1,123 @@
+Tasks = new Mongo.Collection("tasks");
+
+if (Meteor.isServer) {
+ // This code only runs on the server
+ // Only publish tasks that are public or belong to the current user
+ Meteor.publish("tasks", function () {
+ return Tasks.find({
+ $or: [
+ { private: {$ne: true} },
+ { owner: this.userId }
+ ]
+ });
+ });
+}
+
+if (Meteor.isClient) {
+ // This code only runs on the client
+ Meteor.subscribe("tasks");
+
+ Template.body.helpers({
+ tasks: function () {
+ if (Session.get("hideCompleted")) {
+ // If hide completed is checked, filter tasks
+ return Tasks.find({checked: {$ne: true}}, {sort: {createdAt: -1}});
+ } else {
+ // Otherwise, return all of the tasks
+ return Tasks.find({}, {sort: {createdAt: -1}});
+ }
+ },
+ hideCompleted: function () {
+ return Session.get("hideCompleted");
+ },
+ incompleteCount: function () {
+ return Tasks.find({checked: {$ne: true}}).count();
+ }
+ });
+
+ Template.body.events({
+ "submit .new-task": function (event) {
+ // Prevent default browser form submit
+ event.preventDefault();
+
+ // Get value from form element
+ var text = event.target.text.value;
+
+ // Insert a task into the collection
+ Meteor.call("addTask", text);
+
+ // Clear form
+ event.target.text.value = "";
+ },
+ "change .hide-completed input": function (event) {
+ Session.set("hideCompleted", event.target.checked);
+ }
+ });
+
+ Template.task.helpers({
+ isOwner: function () {
+ return this.owner === Meteor.userId();
+ }
+ });
+
+ Template.task.events({
+ "click .toggle-checked": function () {
+ // Set the checked property to the opposite of its current value
+ Meteor.call("setChecked", this._id, ! this.checked);
+ },
+ "click .delete": function () {
+ Meteor.call("deleteTask", this._id);
+ },
+ "click .toggle-private": function () {
+ Meteor.call("setPrivate", this._id, ! this.private);
+ }
+ });
+
+ Accounts.ui.config({
+ passwordSignupFields: "USERNAME_ONLY"
+ });
+}
+
+Meteor.methods({
+ addTask: function (text) {
+ // Make sure the user is logged in before inserting a task
+ if (! Meteor.userId()) {
+ throw new Meteor.Error("not-authorized");
+ }
+
+ Tasks.insert({
+ text: text,
+ createdAt: new Date(),
+ owner: Meteor.userId(),
+ username: Meteor.user().username
+ });
+ },
+ deleteTask: function (taskId) {
+ var task = Tasks.findOne(taskId);
+ if (task.private && task.owner !== Meteor.userId()) {
+ // If the task is private, make sure only the owner can delete it
+ throw new Meteor.Error("not-authorized");
+ }
+
+ Tasks.remove(taskId);
+ },
+ setChecked: function (taskId, setChecked) {
+ var task = Tasks.findOne(taskId);
+ if (task.private && task.owner !== Meteor.userId()) {
+ // If the task is private, make sure only the owner can check it off
+ throw new Meteor.Error("not-authorized");
+ }
+
+ Tasks.update(taskId, { $set: { checked: setChecked} });
+ },
+ setPrivate: function (taskId, setToPrivate) {
+ var task = Tasks.findOne(taskId);
+
+ // Make sure only the task owner can make a task private
+ if (task.owner !== Meteor.userId()) {
+ throw new Meteor.Error("not-authorized");
+ }
+
+ Tasks.update(taskId, { $set: { private: setToPrivate } });
+ }
+});
diff --git a/examples/todos/.meteor/.finished-upgraders b/examples/todos/.meteor/.finished-upgraders
index 8a761038c5..61ee313230 100644
--- a/examples/todos/.meteor/.finished-upgraders
+++ b/examples/todos/.meteor/.finished-upgraders
@@ -6,3 +6,7 @@ notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
+1.2.0-standard-minifiers-package
+1.2.0-meteor-platform-split
+1.2.0-cordova-changes
+1.2.0-breaking-changes
diff --git a/examples/todos/.meteor/release b/examples/todos/.meteor/release
index 315c635bf3..712ef798f5 100644
--- a/examples/todos/.meteor/release
+++ b/examples/todos/.meteor/release
@@ -1 +1 @@
-METEOR@1.1.0.3
+METEOR@1.2
diff --git a/examples/todos/.meteor/versions b/examples/todos/.meteor/versions
index e68384f4ac..b90a704c66 100644
--- a/examples/todos/.meteor/versions
+++ b/examples/todos/.meteor/versions
@@ -1,60 +1,79 @@
-accounts-base@1.2.0
-accounts-password@1.1.1
-autoupdate@1.2.1
-base64@1.0.3
-binary-heap@1.0.3
-blaze@2.1.2
-blaze-tools@1.0.3
-boilerplate-generator@1.0.3
-callback-hook@1.0.3
-check@1.0.5
-ddp@1.1.0
-deps@1.0.7
-ejson@1.0.6
-email@1.0.6
-fastclick@1.0.3
-geojson-utils@1.0.3
-html-tools@1.0.4
-htmljs@1.0.4
-http@1.1.0
-id-map@1.0.3
-insecure@1.0.3
+accounts-base@1.2.1
+accounts-password@1.1.2
+autoupdate@1.2.3
+babel-compiler@5.8.24
+babel-runtime@0.1.4
+base64@1.0.4
+binary-heap@1.0.4
+blaze@2.1.3
+blaze-html-templates@1.0.1
+blaze-tools@1.0.4
+boilerplate-generator@1.0.4
+caching-compiler@1.0.0
+caching-html-compiler@1.0.1
+callback-hook@1.0.4
+check@1.0.6
+ddp@1.2.1
+ddp-client@1.2.1
+ddp-common@1.2.1
+ddp-rate-limiter@1.0.0
+ddp-server@1.2.1
+deps@1.0.8
+diff-sequence@1.0.1
+ecmascript@0.1.3
+ecmascript-collections@0.1.6
+ejson@1.0.7
+email@1.0.7
+fastclick@1.0.7
+geojson-utils@1.0.4
+hot-code-push@1.0.0
+html-tools@1.0.5
+htmljs@1.0.5
+http@1.1.1
+id-map@1.0.4
+insecure@1.0.4
iron:core@0.3.4
iron:dynamic-template@0.4.1
iron:layout@0.4.1
iron:router@0.9.4
-jquery@1.11.3_2
-json@1.0.3
-launch-screen@1.0.2
-less@1.0.14
-livedata@1.0.13
-localstorage@1.0.3
-logging@1.0.7
-meteor@1.1.6
-meteor-platform@1.2.2
-minifiers@1.1.5
-minimongo@1.0.8
-mobile-status-bar@1.0.3
-mongo@1.1.0
+jquery@1.11.4
+launch-screen@1.0.3
+less@2.5.0_1
+livedata@1.0.14
+localstorage@1.0.4
+logging@1.0.8
+meteor@1.1.7
+meteor-base@1.0.1
+minifiers@1.1.6
+minimongo@1.0.9
+mobile-experience@1.0.1
+mobile-status-bar@1.0.6
+mongo@1.1.1
+mongo-id@1.0.1
npm-bcrypt@0.7.8_2
-observe-sequence@1.0.6
-ordered-dict@1.0.3
-random@1.0.3
-reactive-dict@1.1.0
-reactive-var@1.0.5
-reload@1.1.3
-retry@1.0.3
-routepolicy@1.0.5
-service-configuration@1.0.4
-session@1.1.0
-sha@1.0.3
-spacebars@1.0.6
-spacebars-compiler@1.0.6
-srp@1.0.3
-templating@1.1.1
-tracker@1.0.7
-ui@1.0.6
-underscore@1.0.3
-url@1.0.4
-webapp@1.2.0
-webapp-hashing@1.0.3
+npm-mongo@1.4.39_1
+observe-sequence@1.0.7
+ordered-dict@1.0.4
+promise@0.4.8
+random@1.0.4
+rate-limit@1.0.0
+reactive-dict@1.1.1
+reactive-var@1.0.6
+reload@1.1.4
+retry@1.0.4
+routepolicy@1.0.6
+service-configuration@1.0.5
+session@1.1.1
+sha@1.0.4
+spacebars@1.0.7
+spacebars-compiler@1.0.7
+srp@1.0.4
+standard-minifiers@1.0.0
+templating@1.1.2
+templating-tools@1.0.0
+tracker@1.0.8
+ui@1.0.7
+underscore@1.0.4
+url@1.0.5
+webapp@1.2.2
+webapp-hashing@1.0.4
diff --git a/packages/accounts-base/accounts_rate_limit.js b/packages/accounts-base/accounts_rate_limit.js
index 67c81c13f0..77842f012b 100644
--- a/packages/accounts-base/accounts_rate_limit.js
+++ b/packages/accounts-base/accounts_rate_limit.js
@@ -5,7 +5,7 @@ Ap.removeDefaultRateLimit = function () {
const resp = DDPRateLimiter.removeRule(defaultRateLimiterRuleId);
defaultRateLimiterRuleId = null;
return resp;
-}
+};
// Add a default rule of limiting logins, creating new users and password reset
// to 5 times every 10 seconds per connection.
@@ -24,6 +24,6 @@ Ap.addDefaultRateLimit = function () {
}
}, 5, 10000);
}
-}
+};
-Ap.addDefaultRateLimit();
\ No newline at end of file
+Ap.addDefaultRateLimit();
diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js
index f448932eb7..ed426fdef8 100644
--- a/packages/accounts-base/package.js
+++ b/packages/accounts-base/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "A user account system",
- version: "1.2.1-plugins.0"
+ version: "1.2.1"
});
Package.onUse(function (api) {
diff --git a/packages/accounts-facebook/package.js b/packages/accounts-facebook/package.js
index 96cac242a2..4f0c166948 100644
--- a/packages/accounts-facebook/package.js
+++ b/packages/accounts-facebook/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Facebook accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-github/package.js b/packages/accounts-github/package.js
index 581b0a7657..7af5875dbe 100644
--- a/packages/accounts-github/package.js
+++ b/packages/accounts-github/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Github accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-google/package.js b/packages/accounts-google/package.js
index 74834708da..8da49971c6 100644
--- a/packages/accounts-google/package.js
+++ b/packages/accounts-google/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Google accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-meetup/package.js b/packages/accounts-meetup/package.js
index 588c7c9c57..53e25e8013 100644
--- a/packages/accounts-meetup/package.js
+++ b/packages/accounts-meetup/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Meetup accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-meteor-developer/package.js b/packages/accounts-meteor-developer/package.js
index b8764c994d..f4506185f2 100644
--- a/packages/accounts-meteor-developer/package.js
+++ b/packages/accounts-meteor-developer/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Meteor developer accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/accounts-oauth/package.js b/packages/accounts-oauth/package.js
index 4f39fa35f3..e17f3b6dde 100644
--- a/packages/accounts-oauth/package.js
+++ b/packages/accounts-oauth/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for OAuth-based login services",
- version: "1.1.6-plugins.0"
+ version: "1.1.6"
});
Package.onUse(function (api) {
diff --git a/packages/accounts-password/package.js b/packages/accounts-password/package.js
index b45b16f325..ec7dd94b83 100644
--- a/packages/accounts-password/package.js
+++ b/packages/accounts-password/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Password support for accounts",
- version: "1.1.2-plugins.0"
+ version: "1.1.2"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-password/password_client.js b/packages/accounts-password/password_client.js
index ab6cda5e1c..b6aa443695 100644
--- a/packages/accounts-password/password_client.js
+++ b/packages/accounts-password/password_client.js
@@ -130,8 +130,6 @@ Accounts.createUser = function (options, callback) {
});
};
-
-
// Change password. Must be logged in.
//
// @param oldPassword {String|null} By default servers no longer allow
diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js
index cc4a7335de..6e7c1f8f76 100644
--- a/packages/accounts-password/password_server.js
+++ b/packages/accounts-password/password_server.js
@@ -77,13 +77,7 @@ var checkPassword = Accounts._checkPassword;
/// LOGIN
///
-// Attempts to find a user from a user query.
-// First tries to match username or email case sensitively; if that fails, it
-// tries case insensitively; but if more than one user matches the case
-// insensitive search, it returns null
-// @param query {Object} with one of `id`, `username`, or `email`.
-// @returns A user if found, else null
-var findUserFromQuery = function (query) {
+Accounts._findUserByQuery = function (query) {
var user = null;
if (query.id) {
@@ -110,8 +104,6 @@ var findUserFromQuery = function (query) {
// No match if multiple candidates are found
if (candidateUsers.length === 1) {
user = candidateUsers[0];
- } else {
- console.error('Found multiple users with ' + fieldName + ' = ' + fieldValue + ' only differing in case. Requiring case sensitive login.');
}
}
}
@@ -119,6 +111,35 @@ var findUserFromQuery = function (query) {
return user;
};
+/**
+ * @summary Finds the user with the specified username.
+ * First tries to match username case sensitively; if that fails, it
+ * tries case insensitively; but if more than one user matches the case
+ * insensitive search, it returns null.
+ * @locus Server
+ * @param {String} username The username to look for
+ * @returns {Object} A user if found, else null
+ */
+Accounts.findUserByUsername = function (username) {
+ return Accounts._findUserByQuery({
+ username: username
+ });
+};
+
+/**
+ * @summary Finds the user with the specified email.
+ * First tries to match email case sensitively; if that fails, it
+ * tries case insensitively; but if more than one user matches the case
+ * insensitive search, it returns null.
+ * @locus Server
+ * @param {String} email The email address to look for
+ * @returns {Object} A user if found, else null
+ */
+Accounts.findUserByEmail = function (email) {
+ return Accounts._findUserByQuery({
+ email: email
+ });
+};
// Generates a MongoDB selector that can be used to perform a fast case
// insensitive lookup for the given fieldName and string. Since MongoDB does
@@ -164,6 +185,26 @@ var generateCasePermutationsForString = function (string) {
return permutations;
}
+var checkForCaseInsensitiveDuplicates = function (fieldName, displayName, fieldValue, ownUserId) {
+ // Some tests need the ability to add users with the same case insensitive
+ // value, hence the _skipCaseInsensitiveChecksForTest check
+ var skipCheck = _.has(Accounts._skipCaseInsensitiveChecksForTest, fieldValue);
+
+ if (fieldValue && !skipCheck) {
+ var matchedUsers = Meteor.users.find(
+ selectorForFastCaseInsensitiveLookup(fieldName, fieldValue)).fetch();
+
+ if (matchedUsers.length > 0 &&
+ // If we don't have a userId yet, any match we find is a duplicate
+ (!ownUserId ||
+ // Otherwise, check to see if there are multiple matches or a match
+ // that is not us
+ (matchedUsers.length > 1 || matchedUsers[0]._id !== ownUserId))) {
+ throw new Meteor.Error(403, displayName + " already exists.");
+ }
+ }
+};
+
// XXX maybe this belongs in the check package
var NonEmptyString = Match.Where(function (x) {
check(x, String);
@@ -210,7 +251,7 @@ Accounts.registerLoginHandler("password", function (options) {
});
- var user = findUserFromQuery(options.user);
+ var user = Accounts._findUserByQuery(options.user);
if (!user)
throw new Meteor.Error(403, "User not found");
@@ -276,7 +317,7 @@ Accounts.registerLoginHandler("password", function (options) {
password: passwordValidator
});
- var user = findUserFromQuery(options.user);
+ var user = Accounts._findUserByQuery(options.user);
if (!user)
throw new Meteor.Error(403, "User not found");
@@ -320,6 +361,40 @@ Accounts.registerLoginHandler("password", function (options) {
/// CHANGING
///
+/**
+ * @summary Change a user's username. Use this instead of updating the
+ * database directly. The operation will fail if there is an existing user
+ * with a username only differing in case.
+ * @locus Server
+ * @param {String} userId The ID of the user to update.
+ * @param {String} newUsername A new username for the user.
+ */
+Accounts.setUsername = function (userId, newUsername) {
+ check(userId, NonEmptyString);
+ check(newUsername, NonEmptyString);
+
+ var user = Meteor.users.findOne(userId);
+ if (!user)
+ throw new Meteor.Error(403, "User not found");
+
+ var oldUsername = user.username;
+
+ // Perform a case insensitive check fro duplicates before update
+ checkForCaseInsensitiveDuplicates('username', 'Username', newUsername, user._id);
+
+ Meteor.users.update({_id: user._id}, {$set: {username: newUsername}});
+
+ // Perform another check after update, in case a matching user has been
+ // inserted in the meantime
+ try {
+ checkForCaseInsensitiveDuplicates('username', 'Username', newUsername, user._id);
+ } catch (ex) {
+ // Undo update if the check fails
+ Meteor.users.update({_id: user._id}, {$set: {username: oldUsername}});
+ throw ex;
+ }
+};
+
// Let the user change their own password if they know the old
// password. `oldPassword` and `newPassword` should be objects with keys
// `digest` and `algorithm` (representing the SHA256 of the password).
@@ -758,7 +833,111 @@ Meteor.methods({verifyEmail: function (token) {
);
}});
+/**
+ * @summary Add an email address for a user. Use this instead of directly
+ * updating the database. The operation will fail if there is a different user
+ * with an email only differing in case. If the specified user has an existing
+ * email only differing in case however, we replace it.
+ * @locus Server
+ * @param {String} userId The ID of the user to update.
+ * @param {String} newEmail A new email address for the user.
+ * @param {Boolean} [verified] Optional - whether the new email address should
+ * be marked as verified. Defaults to false.
+ */
+Accounts.addEmail = function (userId, newEmail, verified) {
+ check(userId, NonEmptyString);
+ check(newEmail, NonEmptyString);
+ check(verified, Match.Optional(Boolean));
+ if (_.isUndefined(verified)) {
+ verified = false;
+ }
+
+ var user = Meteor.users.findOne(userId);
+ if (!user)
+ throw new Meteor.Error(403, "User not found");
+
+ // Allow users to change their own email to a version with a different case
+
+ // We don't have to call checkForCaseInsensitiveDuplicates to do a case
+ // insensitive check across all emails in the database here because: (1) if
+ // there is no case-insensitive duplicate between this user and other users,
+ // then we are OK and (2) if this would create a conflict with other users
+ // then there would already be a case-insensitive duplicate and we can't fix
+ // that in this code anyway.
+ var caseInsensitiveRegExp =
+ new RegExp('^' + Meteor._escapeRegExp(newEmail) + '$', 'i');
+
+ var didUpdateOwnEmail = _.any(user.emails, function(email, index) {
+ if (caseInsensitiveRegExp.test(email.address)) {
+ Meteor.users.update({
+ _id: user._id,
+ 'emails.address': email.address
+ }, {$set: {
+ 'emails.$.address': newEmail,
+ 'emails.$.verified': verified
+ }});
+ return true;
+ }
+
+ return false;
+ });
+
+ // In the other updates below, we have to do another call to
+ // checkForCaseInsensitiveDuplicates to make sure that no conflicting values
+ // were added to the database in the meantime. We don't have to do this for
+ // the case where the user is updating their email address to one that is the
+ // same as before, but only different because of capitalization. Read the
+ // big comment above to understand why.
+
+ if (didUpdateOwnEmail) {
+ return;
+ }
+
+ // Perform a case insensitive check for duplicates before update
+ checkForCaseInsensitiveDuplicates('emails.address', 'Email', newEmail, user._id);
+
+ Meteor.users.update({
+ _id: user._id
+ }, {
+ $addToSet: {
+ emails: {
+ address: newEmail,
+ verified: verified
+ }
+ }
+ });
+
+ // Perform another check after update, in case a matching user has been
+ // inserted in the meantime
+ try {
+ checkForCaseInsensitiveDuplicates('emails.address', 'Email', newEmail, user._id);
+ } catch (ex) {
+ // Undo update if the check fails
+ Meteor.users.update({_id: user._id},
+ {$pull: {emails: {address: newEmail}}});
+ throw ex;
+ }
+}
+
+/**
+ * @summary Remove an email address for a user. Use this instead of updating
+ * the database directly.
+ * @locus Server
+ * @param {String} userId The ID of the user to update.
+ * @param {String} email The email address to remove.
+ */
+Accounts.removeEmail = function (userId, email) {
+ check(userId, NonEmptyString);
+ check(email, NonEmptyString);
+
+ var user = Meteor.users.findOne(userId);
+ if (!user)
+ throw new Meteor.Error(403, "User not found");
+
+ Meteor.users.update({_id: user._id},
+ {$pull: {emails: {address: email}}});
+}
///
/// CREATING USERS
@@ -794,34 +973,16 @@ var createUser = function (options) {
if (email)
user.emails = [{address: email, verified: false}];
- // Check if there is no other user with a username or email only differing
- // in case.
- var performCaseInsensitiveCheck = function () {
- // Some tests need the ability to add users with the same case insensitive
- // username or email, hence the _skipCaseInsensitiveChecksForTest check
-
- if (username &&
- !_.has(Accounts._skipCaseInsensitiveChecksForTest, username) &&
- Meteor.users.find(selectorForFastCaseInsensitiveLookup(
- "username", username)).count() > 1) {
- throw new Meteor.Error(403, "Username already exists.");
- }
-
- if (email &&
- !_.has(Accounts._skipCaseInsensitiveChecksForTest, email) &&
- Meteor.users.find(selectorForFastCaseInsensitiveLookup(
- "emails.address", email)).count() > 1) {
- throw new Meteor.Error(403, "Email already exists.");
- }
- }
-
// Perform a case insensitive check before insert
- performCaseInsensitiveCheck();
+ checkForCaseInsensitiveDuplicates('username', 'Username', username);
+ checkForCaseInsensitiveDuplicates('emails.address', 'Email', email);
+
var userId = Accounts.insertUserDoc(options, user);
// Perform another check after insert, in case a matching user has been
// inserted in the meantime
try {
- performCaseInsensitiveCheck();
+ checkForCaseInsensitiveDuplicates('username', 'Username', username, userId);
+ checkForCaseInsensitiveDuplicates('emails.address', 'Email', email, userId);
} catch (ex) {
// Remove inserted user if the check fails
Meteor.users.remove(userId);
diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js
index 32c361d9bb..943062b74d 100644
--- a/packages/accounts-password/password_tests.js
+++ b/packages/accounts-password/password_tests.js
@@ -2,9 +2,6 @@ Accounts._noConnectionCloseDelayForTest = true;
Accounts.removeDefaultRateLimit();
if (Meteor.isServer) {
Meteor.methods({
- getUserId: function () {
- return this.userId;
- },
getResetToken: function () {
var token = Meteor.users.findOne(this.userId).services.password.reset;
return token;
@@ -48,14 +45,29 @@ if (Meteor.isClient) (function () {
};
var logoutStep = function (test, expect) {
Meteor.logout(expect(function (error) {
- test.equal(error, undefined);
+ if (error) {
+ test.fail(error.message);
+ }
test.equal(Meteor.user(), null);
}));
};
var loggedInAs = function (someUsername, test, expect) {
return expect(function (error) {
- test.equal(error, undefined);
- test.equal(Meteor.user().username, someUsername);
+ if (error) {
+ test.fail(error.message);
+ }
+ test.equal(Meteor.userId() && Meteor.user().username, someUsername);
+ });
+ };
+ var loggedInUserHasEmail = function (someEmail, test, expect) {
+ return expect(function (error) {
+ if (error) {
+ test.fail(error.message);
+ }
+ var user = Meteor.user();
+ test.isTrue(user && _.some(user.emails, function(email) {
+ return email.address === someEmail;
+ }));
});
};
var expectError = function (expectedError, test, expect) {
@@ -74,17 +86,23 @@ if (Meteor.isClient) (function () {
};
var invalidateLoginsStep = function (test, expect) {
Meteor.call("testInvalidateLogins", 'fail', expect(function (error) {
- test.isFalse(error);
+ if (error) {
+ test.fail(error.message);
+ }
}));
};
var hideActualLoginErrorStep = function (test, expect) {
Meteor.call("testInvalidateLogins", 'hide', expect(function (error) {
- test.isFalse(error);
+ if (error) {
+ test.fail(error.message);
+ }
}));
};
var validateLoginsStep = function (test, expect) {
Meteor.call("testInvalidateLogins", false, expect(function (error) {
- test.isFalse(error);
+ if (error) {
+ test.fail(error.message);
+ }
}));
};
@@ -206,7 +224,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive username with non-ASCII characters", [
+ testAsyncMulti("passwords - logging in with case insensitive username " +
+ "with non-ASCII characters", [
function (test, expect) {
// Hack because Tinytest does not clean the database between tests/runs
this.randomSuffix = Random.id(10);
@@ -226,7 +245,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive username should escape regex special characters", [
+ testAsyncMulti("passwords - logging in with case insensitive username " +
+ "should escape regex special characters", [
createUserStep,
logoutStep,
// We shouldn't be able to log in with a regex expression for the username
@@ -238,7 +258,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive username should require a match of the full string", [
+ testAsyncMulti("passwords - logging in with case insensitive username " +
+ "should require a match of the full string", [
createUserStep,
logoutStep,
// We shouldn't be able to log in with a partial match for the username
@@ -250,21 +271,22 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive username when there are multiple matches", [
+ testAsyncMulti("passwords - logging in with case insensitive username when " +
+ "there are multiple matches", [
createUserStep,
logoutStep,
function (test, expect) {
- this.otherUserName = 'Adalovelace' + this.randomSuffix;
- addSkipCaseInsensitiveChecksForTest(this.otherUserName, test, expect);
+ this.otherUsername = 'Adalovelace' + this.randomSuffix;
+ addSkipCaseInsensitiveChecksForTest(this.otherUsername, test, expect);
},
// Create another user with a username that only differs in case
function (test, expect) {
Accounts.createUser(
- { username: this.otherUserName, password: this.password },
- loggedInAs(this.otherUserName, test, expect));
+ { username: this.otherUsername, password: this.password },
+ loggedInAs(this.otherUsername, test, expect));
},
function (test, expect) {
- removeSkipCaseInsensitiveChecksForTest(this.otherUserName, test, expect);
+ removeSkipCaseInsensitiveChecksForTest(this.otherUsername, test, expect);
},
// We shouldn't be able to log in with the username in lower case
function (test, expect) {
@@ -282,7 +304,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - creating users with the same case insensitive username", [
+ testAsyncMulti("passwords - creating users with the same case insensitive " +
+ "username", [
createUserStep,
logoutStep,
// Attempting to create another user with a username that only differs in
@@ -318,7 +341,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive email should escape regex special characters", [
+ testAsyncMulti("passwords - logging in with case insensitive email should " +
+ "escape regex special characters", [
createUserStep,
logoutStep,
// We shouldn't be able to log in with a regex expression for the email
@@ -330,7 +354,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive email should require a match of the full string", [
+ testAsyncMulti("passwords - logging in with case insensitive email should " +
+ "require a match of the full string", [
createUserStep,
logoutStep,
// We shouldn't be able to log in with a partial match for the email
@@ -342,24 +367,25 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - logging in with case insensitive email when there are multiple matches", [
+ testAsyncMulti("passwords - logging in with case insensitive email when " +
+ "there are multiple matches", [
createUserStep,
logoutStep,
function (test, expect) {
- this.otherUserName = 'AdaLovelace' + Random.id(10);
+ this.otherUsername = 'AdaLovelace' + Random.id(10);
this.otherEmail = "ADA-intercept@lovelace.com" + this.randomSuffix;
addSkipCaseInsensitiveChecksForTest(this.otherEmail, test, expect);
},
// Create another user with an email that only differs in case
function (test, expect) {
Accounts.createUser(
- { username: this.otherUserName,
+ { username: this.otherUsername,
email: this.otherEmail,
password: this.password },
- loggedInAs(this.otherUserName, test, expect));
+ loggedInAs(this.otherUsername, test, expect));
},
function (test, expect) {
- removeSkipCaseInsensitiveChecksForTest(this.otherUserName, test, expect);
+ removeSkipCaseInsensitiveChecksForTest(this.otherUsername, test, expect);
},
logoutStep,
// We shouldn't be able to log in with the email in lower case
@@ -378,7 +404,8 @@ if (Meteor.isClient) (function () {
}
]);
- testAsyncMulti("passwords - creating users with the same case insensitive email", [
+ testAsyncMulti("passwords - creating users with the same case insensitive " +
+ "email", [
createUserStep,
logoutStep,
// Attempting to create another user with an email that only differs in
@@ -1229,7 +1256,8 @@ if (Meteor.isServer) (function () {
});
});
- // XXX would be nice to test Accounts.config({forbidClientAccountCreation: true})
+ // XXX would be nice to test
+ // Accounts.config({forbidClientAccountCreation: true})
Tinytest.addAsync(
'passwords - login token observes get cleaned up',
@@ -1295,7 +1323,8 @@ if (Meteor.isServer) (function () {
Accounts.sendResetPasswordEmail(userId, email);
- var resetPasswordEmailOptions = Meteor.call("getInterceptedEmails", email)[0];
+ var resetPasswordEmailOptions =
+ Meteor.call("getInterceptedEmails", email)[0];
var re = new RegExp(Meteor.absoluteUrl() + "#/reset-password/(\\S*)");
var match = resetPasswordEmailOptions.text.match(re);
@@ -1312,4 +1341,164 @@ if (Meteor.isServer) (function () {
Meteor.call("login", {user: {username: username}, password: "new-password"});
}, /Incorrect password/);
});
+
+ // We should be able to change the username
+ Tinytest.add("passwords - change username", function (test) {
+ var username = Random.id();
+ var userId = Accounts.createUser({
+ username: username
+ });
+
+ test.isTrue(userId);
+
+ var newUsername = Random.id();
+ Accounts.setUsername(userId, newUsername);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).username, newUsername);
+
+ // Test findUserByUsername as well while we're here
+ test.equal(Accounts.findUserByUsername(newUsername)._id, userId);
+ });
+
+ Tinytest.add("passwords - change username to a new one only differing " +
+ "in case", function (test) {
+ var username = Random.id() + "user";
+ var userId = Accounts.createUser({
+ username: username.toUpperCase()
+ });
+
+ test.isTrue(userId);
+
+ var newUsername = username.toLowerCase();
+ Accounts.setUsername(userId, newUsername);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).username, newUsername);
+ });
+
+ // We should not be able to change the username to one that only
+ // differs in case from an existing one
+ Tinytest.add("passwords - change username should fail when there are " +
+ "existing users with a username only differing in case", function (test) {
+ var username = Random.id() + "user";
+ var usernameUpper = username.toUpperCase();
+
+ var userId1 = Accounts.createUser({
+ username: username
+ });
+
+ var user2OriginalUsername = Random.id();
+ var userId2 = Accounts.createUser({
+ username: user2OriginalUsername
+ });
+
+ test.isTrue(userId1);
+ test.isTrue(userId2);
+
+ test.throws(function () {
+ Accounts.setUsername(userId2, usernameUpper);
+ }, /Username already exists/);
+
+ test.equal(Accounts._findUserByQuery({id: userId2}).username,
+ user2OriginalUsername);
+ });
+
+ Tinytest.add("passwords - add email", function (test) {
+ var origEmail = Random.id() + "@turing.com";
+ var userId = Accounts.createUser({
+ email: origEmail
+ });
+
+ var newEmail = Random.id() + "@turing.com";
+ Accounts.addEmail(userId, newEmail);
+
+ var thirdEmail = Random.id() + "@turing.com";
+ Accounts.addEmail(userId, thirdEmail, true);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).emails, [
+ { address: origEmail, verified: false },
+ { address: newEmail, verified: false },
+ { address: thirdEmail, verified: true }
+ ]);
+
+ // Test findUserByEmail as well while we're here
+ test.equal(Accounts.findUserByEmail(origEmail)._id, userId);
+ });
+
+ Tinytest.add("passwords - add email when the user has an existing email " +
+ "only differing in case", function (test) {
+ var origEmail = Random.id() + "@turing.com";
+ var userId = Accounts.createUser({
+ email: origEmail
+ });
+
+ var newEmail = Random.id() + "@turing.com";
+ Accounts.addEmail(userId, newEmail);
+
+ var thirdEmail = origEmail.toUpperCase();
+ Accounts.addEmail(userId, thirdEmail, true);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).emails, [
+ { address: thirdEmail, verified: true },
+ { address: newEmail, verified: false }
+ ]);
+ });
+
+ Tinytest.add("passwords - add email should fail when there is an existing " +
+ "user with an email only differing in case", function (test) {
+ var user1Email = Random.id() + "@turing.com";
+ var userId1 = Accounts.createUser({
+ email: user1Email
+ });
+
+ var user2Email = Random.id() + "@turing.com";
+ var userId2 = Accounts.createUser({
+ email: user2Email
+ });
+
+ var dupEmail = user1Email.toUpperCase();
+ test.throws(function () {
+ Accounts.addEmail(userId2, dupEmail);
+ }, /Email already exists/);
+
+ test.equal(Accounts._findUserByQuery({id: userId1}).emails, [
+ { address: user1Email, verified: false }
+ ]);
+
+ test.equal(Accounts._findUserByQuery({id: userId2}).emails, [
+ { address: user2Email, verified: false }
+ ]);
+ });
+
+ Tinytest.add("passwords - remove email", function (test) {
+ var origEmail = Random.id() + "@turing.com";
+ var userId = Accounts.createUser({
+ email: origEmail
+ });
+
+ var newEmail = Random.id() + "@turing.com";
+ Accounts.addEmail(userId, newEmail);
+
+ var thirdEmail = Random.id() + "@turing.com";
+ Accounts.addEmail(userId, thirdEmail, true);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).emails, [
+ { address: origEmail, verified: false },
+ { address: newEmail, verified: false },
+ { address: thirdEmail, verified: true }
+ ]);
+
+ Accounts.removeEmail(userId, newEmail);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).emails, [
+ { address: origEmail, verified: false },
+ { address: thirdEmail, verified: true }
+ ]);
+
+ Accounts.removeEmail(userId, origEmail);
+
+ test.equal(Accounts._findUserByQuery({id: userId}).emails, [
+ { address: thirdEmail, verified: true }
+ ]);
+ });
+
}) ();
diff --git a/packages/accounts-twitter/package.js b/packages/accounts-twitter/package.js
index c7bcab74f5..db98ddcaf9 100644
--- a/packages/accounts-twitter/package.js
+++ b/packages/accounts-twitter/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Twitter accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/accounts-ui-unstyled/login_buttons_dropdown.js b/packages/accounts-ui-unstyled/login_buttons_dropdown.js
index 85c112e14e..74d33d3052 100644
--- a/packages/accounts-ui-unstyled/login_buttons_dropdown.js
+++ b/packages/accounts-ui-unstyled/login_buttons_dropdown.js
@@ -146,13 +146,18 @@ Template._loginButtonsLoggedOutDropdown.events({
// force the ui to update so that we have the approprate fields to fill in
Tracker.flush();
- if (document.getElementById('login-username'))
+ if (document.getElementById('login-username') && username !== null)
document.getElementById('login-username').value = username;
- if (document.getElementById('login-email'))
+ if (document.getElementById('login-email') && email !== null)
document.getElementById('login-email').value = email;
- if (document.getElementById('login-username-or-email'))
- document.getElementById('login-username-or-email').value = email || username;
+ var usernameOrEmailInput = document.getElementById('login-username-or-email');
+ if (usernameOrEmailInput) {
+ if (email !== null)
+ usernameOrEmailInput.value = email;
+ if (username !== null)
+ usernameOrEmailInput.value = username;
+ }
if (password !== null)
document.getElementById('login-password').value = password;
diff --git a/packages/accounts-ui-unstyled/package.js b/packages/accounts-ui-unstyled/package.js
index 102eda9637..6aa9a1e521 100644
--- a/packages/accounts-ui-unstyled/package.js
+++ b/packages/accounts-ui-unstyled/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Unstyled version of login widgets",
- version: "1.1.8-plugins.0"
+ version: "1.1.8"
});
Package.onUse(function (api) {
diff --git a/packages/accounts-ui/package.js b/packages/accounts-ui/package.js
index cfd5c10ae3..e16d3e1e11 100644
--- a/packages/accounts-ui/package.js
+++ b/packages/accounts-ui/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Simple templates to add login widgets to an app",
- version: "1.1.6-plugins.1"
+ version: "1.1.6"
});
Package.onUse(function (api) {
diff --git a/packages/accounts-weibo/package.js b/packages/accounts-weibo/package.js
index a6a68110cd..0a2e3b3392 100644
--- a/packages/accounts-weibo/package.js
+++ b/packages/accounts-weibo/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Sina Weibo accounts",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/appcache/package.js b/packages/appcache/package.js
index 327a7b89f0..4de2fee503 100644
--- a/packages/appcache/package.js
+++ b/packages/appcache/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Enable the application cache in the browser",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/audit-argument-checks/package.js b/packages/audit-argument-checks/package.js
index 33987cde86..54ff0ca13d 100644
--- a/packages/audit-argument-checks/package.js
+++ b/packages/audit-argument-checks/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Try to detect inadequate input sanitization",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
// This package is empty; its presence is detected by livedata.
diff --git a/packages/autopublish/package.js b/packages/autopublish/package.js
index 483e6a4473..f5e68c7ff3 100644
--- a/packages/autopublish/package.js
+++ b/packages/autopublish/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "(For prototyping only) Publish the entire database to all clients",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
// This package is empty; its presence is detected by several other packages
diff --git a/packages/autoupdate/QA.md b/packages/autoupdate/QA.md
index 0845c4e27f..62afdc6cdf 100644
--- a/packages/autoupdate/QA.md
+++ b/packages/autoupdate/QA.md
@@ -64,11 +64,11 @@ Undo previous changes made, such as by using `git checkout .` Reload
the client, which will cause the browser to stop using the app cache.
It's hard to see the `newClientAvailable` reactive variable when the
-client automatically reloads. Remove the `reload` package so you can
+client automatically reloads. Remove the `hot-code-push` package so you can
see the variable without having the client also reload.
- $ meteor remove hot-code-push
- $ meteor add autoupdate
+ $ meteor remove meteor-base
+ $ meteor add meteor webapp ddp autoupdate
Add to leaderboard.js:
diff --git a/packages/autoupdate/package.js b/packages/autoupdate/package.js
index fc5b389110..eea8df73bf 100644
--- a/packages/autoupdate/package.js
+++ b/packages/autoupdate/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Update the client when new client code is available",
- version: '1.2.2-plugins.1'
+ version: '1.2.3'
});
Cordova.depends({
diff --git a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json
index a131c8d211..981b99d72b 100644
--- a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json
+++ b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json
@@ -30,25 +30,22 @@
"asap": {
"version": "2.0.3"
},
- "ast-types": {
- "version": "0.8.5"
- },
"ast-traverse": {
"version": "0.1.1"
},
+ "ast-types": {
+ "version": "0.8.5"
+ },
"async": {
"version": "0.2.10"
},
"babel-plugin-constant-folding": {
"version": "1.0.1"
},
- "babel-plugin-eval": {
- "version": "1.0.1"
- },
"babel-plugin-dead-code-elimination": {
"version": "1.0.2"
},
- "babel-plugin-inline-environment-variables": {
+ "babel-plugin-eval": {
"version": "1.0.1"
},
"babel-plugin-jscript": {
@@ -57,6 +54,9 @@
"babel-plugin-member-expression-literals": {
"version": "1.0.1"
},
+ "babel-plugin-inline-environment-variables": {
+ "version": "1.0.1"
+ },
"babel-plugin-property-literals": {
"version": "1.0.1"
},
@@ -66,12 +66,12 @@
"babel-plugin-react-constant-elements": {
"version": "1.0.3"
},
- "babel-plugin-react-display-name": {
- "version": "1.0.3"
- },
"babel-plugin-remove-console": {
"version": "1.0.1"
},
+ "babel-plugin-react-display-name": {
+ "version": "1.0.3"
+ },
"babel-plugin-remove-debugger": {
"version": "1.0.1"
},
@@ -99,12 +99,12 @@
"breakable": {
"version": "1.0.0"
},
- "camelcase": {
- "version": "1.2.1"
- },
"chalk": {
"version": "1.1.1"
},
+ "camelcase": {
+ "version": "1.2.1"
+ },
"character-parser": {
"version": "1.2.1"
},
@@ -132,24 +132,24 @@
"css-stringify": {
"version": "1.0.5"
},
- "debug": {
- "version": "2.2.0"
- },
"decamelize": {
"version": "1.0.0"
},
+ "debug": {
+ "version": "2.2.0"
+ },
"detect-indent": {
"version": "3.0.1"
},
"diff": {
"version": "1.4.0"
},
- "escape-string-regexp": {
- "version": "1.0.3"
- },
"esprima-fb": {
"version": "15001.1.0-dev-harmony-fb"
},
+ "escape-string-regexp": {
+ "version": "1.0.3"
+ },
"esutils": {
"version": "2.0.2"
},
@@ -159,12 +159,12 @@
"get-stdin": {
"version": "4.0.1"
},
- "globals": {
- "version": "6.4.1"
- },
"graceful-fs": {
"version": "3.0.8"
},
+ "globals": {
+ "version": "6.4.1"
+ },
"graceful-readlink": {
"version": "1.0.1"
},
@@ -174,27 +174,27 @@
"has-ansi": {
"version": "2.0.0"
},
- "home-or-tmp": {
- "version": "1.0.0"
- },
"iconv-lite": {
"version": "0.4.11"
},
+ "home-or-tmp": {
+ "version": "1.0.0"
+ },
"inflight": {
"version": "1.0.4"
},
"inherits": {
"version": "2.0.1"
},
- "is-finite": {
- "version": "1.0.1"
- },
"install": {
"version": "0.1.8"
},
"is-integer": {
"version": "1.0.6"
},
+ "is-finite": {
+ "version": "1.0.1"
+ },
"is-promise": {
"version": "2.1.0"
},
@@ -219,24 +219,24 @@
"lodash": {
"version": "3.10.1"
},
- "lru-cache": {
- "version": "2.7.0"
- },
"minimatch": {
"version": "2.0.10"
},
+ "lru-cache": {
+ "version": "2.7.0"
+ },
"minimist": {
"version": "1.2.0"
},
"ms": {
"version": "0.7.1"
},
- "number-is-nan": {
- "version": "1.0.0"
- },
"once": {
"version": "1.3.2"
},
+ "number-is-nan": {
+ "version": "1.0.0"
+ },
"optimist": {
"version": "0.3.7"
},
@@ -285,12 +285,12 @@
"resolve": {
"version": "1.1.6"
},
- "shebang-regex": {
- "version": "1.0.0"
- },
"sigmund": {
"version": "1.0.1"
},
+ "shebang-regex": {
+ "version": "1.0.0"
+ },
"simple-fmt": {
"version": "0.1.0"
},
@@ -306,12 +306,12 @@
"stable": {
"version": "0.1.5"
},
- "stringmap": {
- "version": "0.2.2"
- },
"stringset": {
"version": "0.2.1"
},
+ "stringmap": {
+ "version": "0.2.2"
+ },
"strip-ansi": {
"version": "3.0.0"
},
@@ -373,14 +373,6 @@
}
}
},
- "glob": {
- "version": "4.2.2",
- "dependencies": {
- "minimatch": {
- "version": "1.0.0"
- }
- }
- },
"jade": {
"version": "1.11.0",
"dependencies": {
@@ -389,6 +381,14 @@
}
}
},
+ "glob": {
+ "version": "4.2.2",
+ "dependencies": {
+ "minimatch": {
+ "version": "1.0.0"
+ }
+ }
+ },
"jstransformer": {
"version": "0.0.2",
"dependencies": {
diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js
index 33b5a03eca..e5b5ffd778 100644
--- a/packages/babel-compiler/package.js
+++ b/packages/babel-compiler/package.js
@@ -3,7 +3,7 @@ Package.describe({
summary: "Parser/transpiler for ECMAScript 2015+ syntax",
// Tracks the npm version below. Use wrap numbers to increment
// without incrementing the npm version.
- version: '5.8.24-plugins.0'
+ version: '5.8.24'
});
Npm.depends({
diff --git a/packages/base64/package.js b/packages/base64/package.js
index 0f1f39ed42..6e37b01884 100644
--- a/packages/base64/package.js
+++ b/packages/base64/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Base64 encoding and decoding",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/binary-heap/package.js b/packages/binary-heap/package.js
index 03534af583..da8ec81e1c 100644
--- a/packages/binary-heap/package.js
+++ b/packages/binary-heap/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Binary Heap datastructure implementation",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/blaze-html-templates/package.js b/packages/blaze-html-templates/package.js
index 12f570a747..2268dbe766 100644
--- a/packages/blaze-html-templates/package.js
+++ b/packages/blaze-html-templates/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'blaze-html-templates',
- version: '1.0.0',
+ version: '1.0.1',
// Brief, one-line summary of the package.
summary: 'Compile HTML templates into reactive UI with Meteor Blaze',
// By default, Meteor will default to using README.md for documentation.
diff --git a/packages/blaze-tools/package.js b/packages/blaze-tools/package.js
index c72e2061a1..e7f5199b79 100644
--- a/packages/blaze-tools/package.js
+++ b/packages/blaze-tools/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Compile-time tools for Blaze",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/blaze/builtins.js b/packages/blaze/builtins.js
index 3608baf9d1..4d883bcd97 100644
--- a/packages/blaze/builtins.js
+++ b/packages/blaze/builtins.js
@@ -171,16 +171,15 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) {
}, {
addedAt: function (id, item, index) {
Tracker.nonreactive(function () {
- var newDataContext;
+ var newItemView;
if (eachView.variableName) {
// new-style #each (as in {{#each item in items}})
- // the new data context is the same
- newDataContext = Blaze.getData(eachView);
+ // doesn't create a new data context
+ newItemView = Blaze.View('item', eachView.contentFunc);
} else {
- newDataContext = item;
+ newItemView = Blaze.With(item, eachView.contentFunc);
}
- var newItemView = Blaze.With(newDataContext, eachView.contentFunc);
eachView.numItems++;
var bindings = {};
diff --git a/packages/blaze/package.js b/packages/blaze/package.js
index f4e05e0471..b3e602233d 100644
--- a/packages/blaze/package.js
+++ b/packages/blaze/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor Reactive Templating library",
- version: '2.1.3-plugins.0'
+ version: '2.1.3'
});
Package.onUse(function (api) {
diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js
index a0ae8f1044..ec27371d49 100644
--- a/packages/boilerplate-generator/package.js
+++ b/packages/boilerplate-generator/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Generates the boilerplate html from program's manifest",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
@@ -12,7 +12,8 @@ Package.onUse(function (api) {
// spacebars compiler rather than letting the 'templating' package (which
// isn't fully supported on the server yet) handle it. That also means that
// they don't contain the outer "" tag.
- api.addFiles(['boilerplate_web.browser.html',
- 'boilerplate_web.cordova.html'],
- 'server', {isAsset: true});
+ api.addAssets([
+ 'boilerplate_web.browser.html',
+ 'boilerplate_web.cordova.html'
+ ], 'server');
});
diff --git a/packages/browser-policy-common/package.js b/packages/browser-policy-common/package.js
index e31410f64d..02c0d336ce 100644
--- a/packages/browser-policy-common/package.js
+++ b/packages/browser-policy-common/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for browser-policy packages",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/browser-policy-content/package.js b/packages/browser-policy-content/package.js
index 3d90517bef..8a134cc62d 100644
--- a/packages/browser-policy-content/package.js
+++ b/packages/browser-policy-content/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Configure content security policies",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/browser-policy-framing/package.js b/packages/browser-policy-framing/package.js
index c5df10ed15..15d27f9b59 100644
--- a/packages/browser-policy-framing/package.js
+++ b/packages/browser-policy-framing/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Restrict which websites can frame your app",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/browser-policy/package.js b/packages/browser-policy/package.js
index 71102f0aca..26a9075d79 100644
--- a/packages/browser-policy/package.js
+++ b/packages/browser-policy/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Configure security policies enforced by the browser",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/caching-compiler/package.js b/packages/caching-compiler/package.js
index 9f73c897d3..f52b321fb5 100644
--- a/packages/caching-compiler/package.js
+++ b/packages/caching-compiler/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'caching-compiler',
- version: '1.0.0-plugins.1',
+ version: '1.0.0',
summary: 'An easy way to make compiler plugins cache',
documentation: 'README.md'
});
diff --git a/packages/caching-html-compiler/caching-html-compiler.js b/packages/caching-html-compiler/caching-html-compiler.js
index b4c2456ab2..2d99c95fbc 100644
--- a/packages/caching-html-compiler/caching-html-compiler.js
+++ b/packages/caching-html-compiler/caching-html-compiler.js
@@ -107,12 +107,19 @@ CachingHtmlCompiler = class CachingHtmlCompiler extends CachingCompiler {
}
});
- // Add JavaScript code to set attributes on body
+ const setAttributesOnBody = _.map(compileResult.bodyAttrs, (val, key) => {
+ return `document.body.setAttribute('${key}', '${val}');`
+ }).join("\n ");
+
+ // Add JavaScript code to set attributes on body. Don't use JQuery here
+ // because not all users of this package will have it
allJavaScript += `
-Meteor.startup(function() { $('body').attr(${JSON.stringify(compileResult.bodyAttrs)}); });
+Meteor.startup(function() {
+ ${setAttributesOnBody}
+});
`;
}
-
+
if (allJavaScript) {
const filePath = inputFile.getPathInPackage();
@@ -128,7 +135,7 @@ Meteor.startup(function() { $('body').attr(${JSON.stringify(compileResult.bodyAt
// XXX generate a source map
inputFile.addJavaScript({
- path: path.join(pathPart, "template." + basename + ".js"),
+ path: path.join(pathPart, basename + ".compiled.html.js"),
data: allJavaScript
});
}
diff --git a/packages/caching-html-compiler/package.js b/packages/caching-html-compiler/package.js
index b601d98e61..0bc42f3355 100644
--- a/packages/caching-html-compiler/package.js
+++ b/packages/caching-html-compiler/package.js
@@ -1,5 +1,5 @@
Package.describe({
- version: '1.0.0',
+ version: '1.0.1',
// Brief, one-line summary of the package.
summary: 'Pluggable class for compiling HTML into templates',
// By default, Meteor will default to using README.md for documentation.
diff --git a/packages/callback-hook/package.js b/packages/callback-hook/package.js
index d83e31b4be..5bb257ef9d 100644
--- a/packages/callback-hook/package.js
+++ b/packages/callback-hook/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Register callbacks on a hook",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/check/package.js b/packages/check/package.js
index cf9b2d1c2e..70511086d1 100644
--- a/packages/check/package.js
+++ b/packages/check/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Check whether a value matches a pattern",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/code-prettify/package.js b/packages/code-prettify/package.js
index 20c0777966..dc4223a0df 100644
--- a/packages/code-prettify/package.js
+++ b/packages/code-prettify/package.js
@@ -8,7 +8,7 @@ var path = Npm.require('path');
Package.describe({
summary: "Syntax highlighting of code, from Google",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
// XXX this code dumps symbols into the global namespace (directly
diff --git a/packages/coffeescript-test-helper/package.js b/packages/coffeescript-test-helper/package.js
index d17a25a067..b9b7a052e4 100644
--- a/packages/coffeescript-test-helper/package.js
+++ b/packages/coffeescript-test-helper/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Used by the coffeescript package's tests",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/coffeescript/package.js b/packages/coffeescript/package.js
index 44790fc8b7..8bc25dca86 100644
--- a/packages/coffeescript/package.js
+++ b/packages/coffeescript/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Javascript dialect with fewer braces and semicolons",
- version: "1.0.7-plugins.1"
+ version: "1.0.8"
});
Package.registerBuildPlugin({
diff --git a/packages/constraint-solver/package.js b/packages/constraint-solver/package.js
index 8c6c7edd5e..c9bd0f43ee 100644
--- a/packages/constraint-solver/package.js
+++ b/packages/constraint-solver/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Given the set of the constraints, picks a satisfying configuration",
- version: "1.0.20-plugins.0"
+ version: "1.0.20"
});
Package.onUse(function (api) {
diff --git a/packages/crosswalk/package.js b/packages/crosswalk/package.js
index 5f10aa6fbd..8f52eb8a88 100644
--- a/packages/crosswalk/package.js
+++ b/packages/crosswalk/package.js
@@ -1,7 +1,8 @@
Package.describe({
summary: "Makes your Cordova application use the Crosswalk WebView \
instead of the System WebView on Android",
- version: '1.2.0'
+ version: '1.2.0',
+ documentation: null
});
Cordova.depends({
diff --git a/packages/ddp-client/package.js b/packages/ddp-client/package.js
index 2e30e78f3c..41817e27a1 100644
--- a/packages/ddp-client/package.js
+++ b/packages/ddp-client/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's latency-compensated distributed data client",
- version: '1.2.0-plugins.0',
+ version: '1.2.1',
documentation: null
});
diff --git a/packages/ddp-common/package.js b/packages/ddp-common/package.js
index 78d37d9cf2..66d77018af 100644
--- a/packages/ddp-common/package.js
+++ b/packages/ddp-common/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Code shared beween ddp-client and ddp-server",
- version: '1.2.0-plugins.0',
+ version: '1.2.1',
documentation: null
});
diff --git a/packages/ddp-rate-limiter/ddp-rate-limiter.js b/packages/ddp-rate-limiter/ddp-rate-limiter.js
index 3eb4ac9c26..cc1d5a5951 100644
--- a/packages/ddp-rate-limiter/ddp-rate-limiter.js
+++ b/packages/ddp-rate-limiter/ddp-rate-limiter.js
@@ -1,12 +1,12 @@
// Rate Limiter built into DDP with a default error message. See README or
// online documentation for more details.
-DDPRateLimiter = {}
+DDPRateLimiter = {};
var errorMessage = function (rateLimitResult) {
return "Error, too many requests. Please slow down. You must wait " +
Math.ceil(rateLimitResult.timeToReset / 1000) + " seconds before " +
"trying again.";
-}
+};
var rateLimiter = new RateLimiter();
DDPRateLimiter.getErrorMessage = function (rateLimitResult) {
@@ -14,7 +14,7 @@ DDPRateLimiter.getErrorMessage = function (rateLimitResult) {
return errorMessage(rateLimitResult);
else
return errorMessage;
-}
+};
/**
* @summary Set error message text when method or subscription rate limit
@@ -26,7 +26,7 @@ DDPRateLimiter.getErrorMessage = function (rateLimitResult) {
*/
DDPRateLimiter.setErrorMessage = function (message) {
errorMessage = message;
-}
+};
/**
* @summary
@@ -70,7 +70,7 @@ DDPRateLimiter.addRule = function (matcher, numRequests, timeInterval) {
DDPRateLimiter.printRules = function () {
return rateLimiter.rules;
-}
+};
/**
* @summary Removes the specified rule from the rate limiter. If rule had
@@ -80,14 +80,14 @@ DDPRateLimiter.printRules = function () {
*/
DDPRateLimiter.removeRule = function (id) {
return rateLimiter.removeRule(id);
-}
+};
// This is accessed inside livedata_server.js, but shouldn't be called by any
// user.
DDPRateLimiter._increment = function (input) {
rateLimiter.increment(input);
-}
+};
DDPRateLimiter._check = function (input) {
return rateLimiter.check(input);
-}
\ No newline at end of file
+};
diff --git a/packages/ddp-rate-limiter/package.js b/packages/ddp-rate-limiter/package.js
index c732d904ba..a7f86cdf2b 100644
--- a/packages/ddp-rate-limiter/package.js
+++ b/packages/ddp-rate-limiter/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'ddp-rate-limiter',
- version: '0.0.1-plugins.0',
+ version: '1.0.0',
// Brief, one-line summary of the package.
summary: 'The DDPRateLimiter allows users to add rate limits to DDP' +
' methods and subscriptions.',
diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js
index a1f0d50d70..4054483237 100644
--- a/packages/ddp-server/livedata_server.js
+++ b/packages/ddp-server/livedata_server.js
@@ -596,7 +596,7 @@ _.extend(Session.prototype, {
};
DDPRateLimiter._increment(rateLimiterInput);
- var rateLimitResult = DDPRateLimiter._check(rateLimiterInput)
+ var rateLimitResult = DDPRateLimiter._check(rateLimiterInput);
if (!rateLimitResult.allowed) {
self.send({
msg: 'nosub', id: msg.id,
diff --git a/packages/ddp-server/package.js b/packages/ddp-server/package.js
index 28f0d27bba..f1ca546f3a 100644
--- a/packages/ddp-server/package.js
+++ b/packages/ddp-server/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's latency-compensated distributed data server",
- version: '1.2.0-plugins.1',
+ version: '1.2.1',
documentation: null
});
diff --git a/packages/ddp/package.js b/packages/ddp/package.js
index c32ff70dc5..7acdc2ccb0 100644
--- a/packages/ddp/package.js
+++ b/packages/ddp/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's latency-compensated distributed data framework",
- version: '1.2.0-plugins.0'
+ version: '1.2.1'
});
Package.onUse(function (api) {
diff --git a/packages/deps/package.js b/packages/deps/package.js
index 0437f00ce8..ca613f8acb 100644
--- a/packages/deps/package.js
+++ b/packages/deps/package.js
@@ -2,7 +2,7 @@
Package.describe({
summary: "Deprecated: Use the 'tracker' package instead.",
- version: '1.0.8-plugins.0'
+ version: '1.0.8'
});
Package.onUse(function (api) {
diff --git a/packages/diff-sequence/package.js b/packages/diff-sequence/package.js
index 8b45e84c3c..ca1ccc950a 100644
--- a/packages/diff-sequence/package.js
+++ b/packages/diff-sequence/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "An implementation of a diff algorithm on arrays and objects.",
- version: '1.0.0-plugins.0',
+ version: '1.0.1',
documentation: null
});
diff --git a/packages/disable-oplog/package.js b/packages/disable-oplog/package.js
index f84d0d988d..d0f194de6a 100644
--- a/packages/disable-oplog/package.js
+++ b/packages/disable-oplog/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Disables oplog tailing",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
// This package is empty; its presence is detected by mongo-livedata.
diff --git a/packages/ecmascript-collections/package.js b/packages/ecmascript-collections/package.js
index 7a613146d6..57cc38db06 100644
--- a/packages/ecmascript-collections/package.js
+++ b/packages/ecmascript-collections/package.js
@@ -26,6 +26,7 @@ Package.onUse(function(api) {
Package.onTest(function(api) {
api.use("tinytest");
api.use("check");
+ api.use("es5-shim");
api.use("ecmascript-collections");
api.addFiles("collections-tests.js");
});
diff --git a/packages/ecmascript/README.md b/packages/ecmascript/README.md
index b5d79408d5..9d8182d988 100644
--- a/packages/ecmascript/README.md
+++ b/packages/ecmascript/README.md
@@ -1,3 +1,18 @@
+# [ecmascript](https://github.com/meteor/meteor/tree/devel/packages/ecmascript)
+
+This package lets you use new JavaScript language features that are part
+of the [ECMAScript 2015
+specification](http://www.ecma-international.org/ecma-262/6.0/) but are
+not yet supported by all engines or browsers. Unsupported syntax is
+automatically translated into standard JavaScript that behaves the same
+way.
+
+[This video](https://www.youtube.com/watch?v=05Z6YGiZKmE) from the July
+2015 Meteor Devshop gives an overview of how the package works, and what
+it provides.
+
+## Usage
+
The `ecmascript` package registers a compiler plugin that transpiles
ECMAScript 2015+ to ECMAScript 5 (standard JS) in all `.js` files. By
default, this package is pre-installed for all new apps and packages.
@@ -18,3 +33,236 @@ Package.onUse(function (api) {
api.use('ecmascript');
});
```
+
+## Supported ES2015 Features
+
+### Syntax
+
+The `ecmascript` package uses [Babel](http://babeljs.io/) to compile
+ES2015 syntax to ES5 syntax. Many but not all ES2015 features can be
+simulated by Babel, and `ecmascript` enables most of the features
+supported by Babel.
+
+Here is a list of the Babel transformers that are currently enabled:
+
+* [`es3.propertyLiterals`](https://babeljs.io/docs/advanced/transformers/es3/property-literals/)
+ Makes it safe to use reserved keywords like `catch` as unquoted keys in
+ object literals. For example, `{ catch: 123 }` is translated to `{ "catch": 123 }`.
+
+* [`es3.memberExpressionLiterals`](https://babeljs.io/docs/advanced/transformers/es3/member-expression-literals/)
+ Makes it safe to use reserved keywords as property names. For
+ example, `object.catch` is translated to `object["catch"]`.
+
+* [`es6.arrowFunctions`](http://babeljs.io/docs/learn-es2015/#arrows)
+ Provides a shorthand for function expressions. For example,
+ `[1, 2, 3].map(x => x + 1)` evaluates to `[2, 3, 4]`. If `this` is used
+ in the body of the arrow function, it will be automatically bound to the
+ value of `this` in the enclosing scope.
+
+* [`es6.literals`](http://babeljs.io/docs/learn-es2015/#binary-and-octal-literals)
+ Adds support for binary and octal numeric literals. For example,
+ `0b111110111 === 503` and `0o767 === 503`.
+
+* [`es6.templateLiterals`](http://babeljs.io/docs/learn-es2015/#template-strings)
+ Enables multi-line strings delimited by backticks instead of quotation
+ marks, with variable interpolation:
+ ```js
+ var name = "Ben";
+ var message = `My name is:
+ ${name}`;
+ ```
+
+* [`es6.classes`](http://babeljs.io/docs/learn-es2015/#classes)
+ Enables `class` syntax:
+ ```js
+ class Base {
+ constructor(a, b) {
+ this.value = a * b;
+ }
+ }
+
+ class Derived extends Base {
+ constructor(a, b) {
+ super(a + 1, b + 1);
+ }
+ }
+
+ var d = new Derived(2, 3);
+ d.value; // 12
+ ```
+
+* [`es6.constants`](https://babeljs.io/docs/learn-es2015/#let-const)
+ Allows defining block-scoped variables that are not allowed to be
+ redefined:
+ ```js
+ const GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
+
+ // This reassignment will be forbidden by the compiler:
+ GOLDEN_RATIO = "new value";
+ ```
+
+* [`es6.blockScoping`](http://babeljs.io/docs/learn-es2015/#let-const)
+ Enables the `let` and `const` keywords as alternatives to `var`. The key
+ difference is that that variables defined using `let` or `const` are
+ visible only within the block where they are declared, rather than being
+ visible anywhere in the enclosing function. For example:
+ ```js
+ function example(condition) {
+ let x = 0;
+ if (condition) {
+ let x = 1;
+ console.log(x);
+ } else {
+ console.log(x);
+ x = 2;
+ }
+ return x;
+ }
+
+ example(true); // logs 1, returns 0
+ example(false); // logs 0, returns 2
+ ```
+
+* [`es6.properties.shorthand`](https://babeljs.io/docs/learn-es2015/#enhanced-object-literals)
+ Allows omitting the value of an object literal property when the desired
+ value is held by a variable that has the same name as the property
+ key. For example, instead of writing `{ x: x, y: y, z: "asdf" }` you can
+ just write `{ x, y, z: "asdf" }`. Methods can also be written without
+ the `: function` property syntax:
+ ```js
+ var obj = {
+ oldWay: function (a, b) { ... },
+ newWay(a, b) { ... }
+ };
+ ```
+
+* [`es6.properties.computed`](http://babeljs.io/docs/learn-es2015/#enhanced-object-literals)
+ Allows object literal properties with dynamically computed keys:
+ ```js
+ var counter = 0;
+ function getKeyName() {
+ return "key" + counter++;
+ }
+
+ var obj = {
+ [getKeyName()]: "zero",
+ [getKeyName()]: "one",
+ };
+
+ obj.key0; // "zero"
+ obj.key1; // "one"
+ ```
+
+* [`es6.parameters`](http://babeljs.io/docs/learn-es2015/#default-rest-spread)
+ Default expressions for function parameters, evaluated whenever the parameter
+ is `undefined`, `...rest` parameters for capturing remaining
+ arguments without using the `arguments` object:
+ ```js
+ function add(a = 0, ...rest) {
+ rest.forEach(n => a += n);
+ return a;
+ }
+
+ add(); // 0
+ add(1, 2, 3); // 6
+ ```
+
+* [`es6.spread`](http://babeljs.io/docs/learn-es2015/#default-rest-spread)
+ Allows an array of arguments to be interpolated into a list of arguments
+ to a function call, `new` expression, or array literal, without using
+ `Function.prototype.apply`:
+ ```js
+ add(1, ...[2, 3, 4], 5); // 15
+ new Node("name", ...children);
+ [1, ...[2, 3, 4], 5]; // [1, 2, 3, 4, 5]
+ ```
+
+* [`es6.forOf`](http://babeljs.io/docs/learn-es2015/#iterators-for-of)
+ Provides an easy way to iterate over the elements of a collection:
+ ```js
+ let sum = 0;
+ for (var x of [1, 2, 3]) {
+ sum += x;
+ }
+ x; // 6
+ ```
+
+* [`es6.destructuring`](http://babeljs.io/docs/learn-es2015/#destructuring)
+ Destructuring is the technique of using an array or object pattern on
+ the left-hand side of an assignment or declaration, in place of the
+ usual variable or parameter, so that certain sub-properties of the value
+ on the right-hand side will be bound to identifiers that appear within the
+ pattern. Perhaps the simplest example is swapping two variables without
+ using a temporary variable:
+ ```js
+ [a, b] = [b, a];
+ ```
+ Extracting a specific property from an object:
+ ```js
+ let { username: name } = user;
+ // is equivalent to
+ let name = user.username;
+ ```
+ Instead of taking a single opaque `options` parameter, a function can
+ use an object destructuring pattern to name the expected options:
+ ```js
+ function run({ command, args, callback }) { ... }
+
+ run({
+ command: "git",
+ args: ["status", "."],
+ callback(error, status) { ... },
+ unused: "whatever"
+ });
+ ```
+
+* [`es7.objectRestSpread`](https://github.com/sebmarkbage/ecmascript-rest-spread)
+ Supports catch-all `...rest` properties in object literal declarations
+ and assignments:
+ ```js
+ let { x, y, ...rest } = { x: 1, y: 2, a: 3, b: 4 };
+ x; // 1
+ y; // 2
+ rest; // { a: 3, b: 4 }
+ ```
+ Also enables `...spread` properties in object literal expressions:
+ ```js
+ let n = { x, y, ...rest };
+ n; // { x: 1, y: 2, a: 3, b: 4 }
+ ```
+
+* [`es7.trailingFunctionCommas`](https://github.com/jeffmo/es-trailing-function-commas)
+ Allows the final parameter of a function to be followed by a comma,
+ provided that parameter is not a `...rest` parameter.
+
+* [`flow`](https://babeljs.io/docs/advanced/transformers/other/flow/)
+ Permits the use of [Flow](http://flowtype.org/) type annotations. These
+ annotations are simply stripped from the code, so they have no effect on
+ the code's behavior, but you can run the `flow` tool over your code to
+ check the types if desired.
+
+### Polyfills
+
+The ECMAScript 2015 standard library has grown to include new APIs and
+data structures, some of which can be implemented ("polyfilled") using
+JavaScript that runs in all engines and browsers today. Here are three new
+constructors that are guaranteed to be available when the `ecmascript`
+package is installed:
+
+* [`Promise`](https://github.com/meteor/promise)
+ A `Promise` allows its owner to wait for a value that might not be
+ available yet. See [this tutorial](https://www.promisejs.org/) for more
+ details about the API and motivation. The Meteor `Promise`
+ implementation is especially useful because it runs all callback
+ functions in recycled `Fiber`s, so you can use any Meteor API, including
+ those that yield (e.g. `HTTP.get`, `Meteor.call`, or `MongoCollection`),
+ and you never have to call `Meteor.bindEnvironment`.
+
+* [`Map`](https://github.com/zloirock/core-js#map)
+ An associative key-value data structure where the keys can be any
+ JavaScript value (not just strings). Lookup and insertion take constant
+ time.
+
+* [`Set`](https://github.com/zloirock/core-js#set)
+ A collection of unique JavaScript values of any type. Lookup and
+ insertion take constant time.
diff --git a/packages/ecmascript/bare-test.js b/packages/ecmascript/bare-test.js
new file mode 100644
index 0000000000..dc78cec4af
--- /dev/null
+++ b/packages/ecmascript/bare-test.js
@@ -0,0 +1,4 @@
+Tinytest.add("ecmascript - bare files work", function (test) {
+ // This is defined in bare-file.js
+ test.equal(exportedFromBareFile, "Yes");
+});
diff --git a/packages/ecmascript/package.js b/packages/ecmascript/package.js
index 4fc9314c39..329d6db42c 100644
--- a/packages/ecmascript/package.js
+++ b/packages/ecmascript/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'ecmascript',
- version: '0.1.3-plugins.5',
+ version: '0.1.3',
summary: 'Compiler plugin that supports ES2015+ in all .js files',
documentation: 'README.md'
});
@@ -25,9 +25,10 @@ Package.onUse(function (api) {
Package.onTest(function (api) {
api.use(["tinytest", "underscore"]);
- api.use(["ecmascript", "babel-compiler"]);
+ api.use(["es5-shim", "ecmascript", "babel-compiler"]);
api.addFiles("runtime-tests.js");
api.addFiles("transpilation-tests.js", "server");
api.addFiles("bare-test-file.js", "client", { bare: true });
+ api.addFiles("bare-test.js", "client");
});
diff --git a/packages/ejson/package.js b/packages/ejson/package.js
index 6c33a62c59..9f67166bf1 100644
--- a/packages/ejson/package.js
+++ b/packages/ejson/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Extended and Extensible JSON library",
- version: '1.0.7-plugins.0'
+ version: '1.0.7'
});
Package.onUse(function (api) {
diff --git a/packages/email/package.js b/packages/email/package.js
index 8ec7e02a33..995f9e901a 100644
--- a/packages/email/package.js
+++ b/packages/email/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Send email messages",
- version: "1.0.7-plugins.0"
+ version: "1.0.7"
});
Npm.depends({
diff --git a/packages/es5-shim/.npm/package/npm-shrinkwrap.json b/packages/es5-shim/.npm/package/npm-shrinkwrap.json
index b2f6cd83a9..44179218c0 100644
--- a/packages/es5-shim/.npm/package/npm-shrinkwrap.json
+++ b/packages/es5-shim/.npm/package/npm-shrinkwrap.json
@@ -1,7 +1,7 @@
{
"dependencies": {
"es5-shim": {
- "version": "4.1.7"
+ "version": "4.1.13"
}
}
}
diff --git a/packages/es5-shim/console.js b/packages/es5-shim/console.js
new file mode 100644
index 0000000000..9d90f5a8fe
--- /dev/null
+++ b/packages/es5-shim/console.js
@@ -0,0 +1,27 @@
+var hasOwn = Object.prototype.hasOwnProperty;
+
+function wrap(method) {
+ var original = console[method];
+ if (original && typeof original === "object") {
+ // Turn callable console method objects into actual functions.
+ console[method] = function () {
+ return Function.prototype.apply.call(
+ original, console, arguments
+ );
+ };
+ }
+}
+
+if (typeof console === "object" &&
+ // In older Internet Explorers, methods like console.log are actually
+ // callable objects rather than functions.
+ typeof console.log === "object") {
+ for (var method in console) {
+ // In most browsers, this hasOwn check will fail for all console
+ // methods anyway, but fortunately in IE8 the method objects we care
+ // about are own properties.
+ if (hasOwn.call(console, method)) {
+ wrap(method);
+ }
+ }
+}
diff --git a/packages/es5-shim/export_globals.js b/packages/es5-shim/export_globals.js
index 1c2b68c7d7..c0b791eedc 100644
--- a/packages/es5-shim/export_globals.js
+++ b/packages/es5-shim/export_globals.js
@@ -7,3 +7,10 @@ if (global.Date !== Date) {
if (global.parseInt !== parseInt) {
global.parseInt = parseInt;
}
+
+var Sp = String.prototype;
+if (Sp.replace !== originalStringReplace) {
+ // Restore the original value of String#replace, because the es5-shim
+ // reimplementation is buggy. See also import_globals.js.
+ Sp.replace = originalStringReplace;
+}
diff --git a/packages/es5-shim/import_globals.js b/packages/es5-shim/import_globals.js
index 488b85d8fb..9b3bb6f3ac 100644
--- a/packages/es5-shim/import_globals.js
+++ b/packages/es5-shim/import_globals.js
@@ -8,3 +8,8 @@ var global = this;
// variables to their appropriate global values.
Date = global.Date;
parseInt = global.parseInt;
+
+// Save the original String#replace method, because es5-shim's
+// reimplementation of it causes problems in markdown/showdown.js.
+// This original method will be restored in export_globals.js.
+originalStringReplace = String.prototype.replace;
diff --git a/packages/es5-shim/package.js b/packages/es5-shim/package.js
index d59fcc58c0..6ecf62e3ff 100644
--- a/packages/es5-shim/package.js
+++ b/packages/es5-shim/package.js
@@ -1,12 +1,12 @@
Package.describe({
name: "es5-shim",
- version: "0.1.0-plugins.2",
+ version: "4.1.13",
summary: "Shims and polyfills to improve ECMAScript 5 support",
documentation: "README.md"
});
Npm.depends({
- "es5-shim": "4.1.7"
+ "es5-shim": "4.1.13"
});
Package.onUse(function(api) {
@@ -17,9 +17,17 @@ Package.onUse(function(api) {
// Initialize Date and parseInt with their initial global values.
api.addFiles("import_globals.js");
- var es5ShimPath = ".npm/package/node_modules/es5-shim/es5-shim.js";
+ // Turn callable console method objects into actual functions.
+ api.addFiles("console.js", "client");
- api.addFiles(es5ShimPath, "client", {
+ var es5ShimPath = ".npm/package/node_modules/es5-shim/es5-shim.js";
+ var es5ShamPath = ".npm/package/node_modules/es5-shim/es5-sham.js";
+
+ api.addFiles([
+ es5ShimPath,
+ // Limited but necessary polyfills for APIs like Object.create.
+ es5ShamPath
+ ], "client", {
// Files in the es5-shim package are already wrapped in closures.
bare: true
});
diff --git a/packages/facebook/facebook_client.js b/packages/facebook/facebook_client.js
index 6438c528db..d057ad44bd 100644
--- a/packages/facebook/facebook_client.js
+++ b/packages/facebook/facebook_client.js
@@ -34,7 +34,7 @@ Facebook.requestCredential = function (options, credentialRequestCompleteCallbac
'https://www.facebook.com/v2.2/dialog/oauth?client_id=' + config.appId +
'&redirect_uri=' + OAuth._redirectUri('facebook', config) +
'&display=' + display + '&scope=' + scope +
- '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl);
+ '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);
OAuth.launchLogin({
loginService: "facebook",
diff --git a/packages/facebook/package.js b/packages/facebook/package.js
index f91dd03d50..0698daddda 100644
--- a/packages/facebook/package.js
+++ b/packages/facebook/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Facebook OAuth flow",
- version: "1.2.2-plugins.0"
+ version: "1.2.2"
});
Package.onUse(function(api) {
diff --git a/packages/facts/package.js b/packages/facts/package.js
index 71efc6fb5d..7398512654 100644
--- a/packages/facts/package.js
+++ b/packages/facts/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Publish internal app statistics",
- version: '1.0.5-plugins.0'
+ version: '1.0.5'
});
Package.onUse(function (api) {
diff --git a/packages/fastclick/package.js b/packages/fastclick/package.js
index 38ce052ed3..6f2ef065f8 100755
--- a/packages/fastclick/package.js
+++ b/packages/fastclick/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Faster touch events on mobile",
- version: '1.0.6-plugins.0'
+ version: '1.0.7'
});
Package.onUse(function (api) {
diff --git a/packages/force-ssl/package.js b/packages/force-ssl/package.js
index 4bed616c4f..f0a3a32a96 100644
--- a/packages/force-ssl/package.js
+++ b/packages/force-ssl/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Require this application to use HTTPS",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/geojson-utils/package.js b/packages/geojson-utils/package.js
index b7566c184c..9dc78798c0 100644
--- a/packages/geojson-utils/package.js
+++ b/packages/geojson-utils/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: 'GeoJSON utility functions (from https://github.com/maxogden/geojson-js-utils)',
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/github/github_client.js b/packages/github/github_client.js
index 1a8b499eee..914465ca2c 100644
--- a/packages/github/github_client.js
+++ b/packages/github/github_client.js
@@ -30,7 +30,7 @@ Github.requestCredential = function (options, credentialRequestCompleteCallback)
'?client_id=' + config.clientId +
'&scope=' + flatScope +
'&redirect_uri=' + OAuth._redirectUri('github', config) +
- '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl);
+ '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);
OAuth.launchLogin({
loginService: "github",
diff --git a/packages/github/package.js b/packages/github/package.js
index e76e1f2ad7..38f37db688 100644
--- a/packages/github/package.js
+++ b/packages/github/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Github OAuth flow",
- version: "1.1.4-plugins.0"
+ version: "1.1.4"
});
Package.onUse(function(api) {
diff --git a/packages/google/package.js b/packages/google/package.js
index 8cc2621d48..86da9ca299 100644
--- a/packages/google/package.js
+++ b/packages/google/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Google OAuth flow",
- version: "1.1.6-plugins.0"
+ version: "1.1.6"
});
Package.onUse(function(api) {
diff --git a/packages/handlebars/package.js b/packages/handlebars/package.js
index d1abdaa366..bd9469a5a8 100644
--- a/packages/handlebars/package.js
+++ b/packages/handlebars/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Deprecated",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/html-tools/package.js b/packages/html-tools/package.js
index 1ece9d6878..dbe7a29fcd 100644
--- a/packages/html-tools/package.js
+++ b/packages/html-tools/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Standards-compliant HTML tools",
- version: '1.0.5-plugins.0'
+ version: '1.0.5'
});
Package.onUse(function (api) {
diff --git a/packages/htmljs/package.js b/packages/htmljs/package.js
index bc8eb8cc53..66b957b99e 100644
--- a/packages/htmljs/package.js
+++ b/packages/htmljs/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Small library for expressing HTML trees",
- version: '1.0.5-plugins.0'
+ version: '1.0.5'
});
Package.onUse(function (api) {
diff --git a/packages/http/package.js b/packages/http/package.js
index a7c5dcf95a..97311ef858 100644
--- a/packages/http/package.js
+++ b/packages/http/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Make HTTP calls to remote servers",
- version: '1.1.1-plugins.0'
+ version: '1.1.1'
});
Npm.depends({request: "2.53.0"});
@@ -27,5 +27,6 @@ Package.onTest(function (api) {
api.addFiles('test_responder.js', 'server');
api.addFiles('httpcall_tests.js', ['client', 'server']);
- api.addFiles('test_static.serveme', 'client', {isAsset: true});
+
+ api.addAssets('test_static.serveme', 'client');
});
diff --git a/packages/id-map/package.js b/packages/id-map/package.js
index 723cc41d10..13e71accc4 100644
--- a/packages/id-map/package.js
+++ b/packages/id-map/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Dictionary data structure allowing non-string keys",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/insecure/package.js b/packages/insecure/package.js
index 4a42028932..30a6e9aec0 100644
--- a/packages/insecure/package.js
+++ b/packages/insecure/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "(For prototyping only) Allow all database writes from the client",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
// This package is empty; its presence is detected by mongo-livedata.
diff --git a/packages/jquery-waypoints/package.js b/packages/jquery-waypoints/package.js
index 6c51172dda..9d4ef02a9f 100644
--- a/packages/jquery-waypoints/package.js
+++ b/packages/jquery-waypoints/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Run a function when the user scrolls past an element",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/jquery/package.js b/packages/jquery/package.js
index cd2871b31f..ef75c4118c 100644
--- a/packages/jquery/package.js
+++ b/packages/jquery/package.js
@@ -1,11 +1,11 @@
Package.describe({
summary: "Manipulate the DOM using CSS selectors",
- // XXX WHOOPS! We accidentally published jquery 1.11.2 as 1.11.3, because we
- // naively thought that "call the version '1.11.2', add a comment saying that
- // the next version should be '1.11.2_1'" would be sufficient to not be
- // missed during the semi-automated version number bumping step. Next time, use `_0` from the start so it's obvious that something weird is happening!
- version: '1.11.3-plugins.0_3' // XXX see above!!!!
+ // This is actually jQuery 1.11.2, but because of people bumping the
+ // patch number instead of the wrap number, we're higher than that.
+ // In fairness, there's no way to make an RC of a new version without
+ // bumping the patch number.
+ version: '1.11.4'
});
Package.onUse(function (api) {
diff --git a/packages/jsparse/package.js b/packages/jsparse/package.js
index 9307328baf..cfcc1b98ba 100644
--- a/packages/jsparse/package.js
+++ b/packages/jsparse/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Full-featured JavaScript parser",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/launch-screen/package.js b/packages/launch-screen/package.js
index 20c0f2401d..a93e16742c 100644
--- a/packages/launch-screen/package.js
+++ b/packages/launch-screen/package.js
@@ -6,7 +6,7 @@ Package.describe({
// between such packages and the build tool.
name: 'launch-screen',
summary: 'Default and customizable launch screen on mobile.',
- version: '1.0.3-plugins.1'
+ version: '1.0.3'
});
Cordova.depends({
diff --git a/packages/less/package.js b/packages/less/package.js
index c925fae495..42b5ffdfef 100644
--- a/packages/less/package.js
+++ b/packages/less/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'less',
- version: '2.5.0-plugins.0_2',
+ version: '2.5.0_1',
summary: 'Leaner CSS language',
documentation: 'README.md'
});
diff --git a/packages/less/plugin/compile-less.js b/packages/less/plugin/compile-less.js
index b9b26253cc..0ee41b249d 100644
--- a/packages/less/plugin/compile-less.js
+++ b/packages/less/plugin/compile-less.js
@@ -78,10 +78,11 @@ class LessCompiler extends MultiFileCachingCompiler {
const compileResult = {css: output.css, sourceMap: output.map};
const referencedImportPaths = [];
output.imports.forEach((path) => {
- if (! allFiles.has(path)) {
- throw Error("Imported an unknown file?");
+ // Some files that show up in output.imports are not actually files; for
+ // example @import url("...");
+ if (allFiles.has(path)) {
+ referencedImportPaths.push(path);
}
- referencedImportPaths.push(path);
});
return {compileResult, referencedImportPaths};
@@ -115,8 +116,15 @@ class MeteorImportLessFileManager extends less.AbstractFileManager {
}
// We want to be the only active FileManager, so claim to support everything.
- supports() {
- return true;
+ supports(filename) {
+ // We shouldn't process files that start with `//` or a protocol because
+ // those are not relative to the app at all; they are probably native
+ // CSS imports
+ if (! filename.match(/^(https?:)?\/\//)) {
+ return true;
+ }
+
+ return false;
}
loadFile(filename, currentDirectory, options, environment, cb) {
diff --git a/packages/less/tests/top.import.less b/packages/less/tests/top.import.less
index 4d9828eecd..b7f1071eda 100644
--- a/packages/less/tests/top.import.less
+++ b/packages/less/tests/top.import.less
@@ -3,3 +3,7 @@
/* Test that package-relative imports work from within a
* package-relative-imported file */
@import "/tests/top3.import.less";
+
+// Make sure regular CSS import doesn't make the compiler explode - this was
+// a regression from 1.1.0.3 caught in QA
+@import url("http://hello.myfonts.net/count/2c4b9d");
diff --git a/packages/livedata/package.js b/packages/livedata/package.js
index cb924f1c17..1d601c601c 100644
--- a/packages/livedata/package.js
+++ b/packages/livedata/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Moved to the 'ddp' package",
- version: '1.0.14-plugins.0'
+ version: '1.0.14'
});
Package.onUse(function (api) {
diff --git a/packages/localstorage/package.js b/packages/localstorage/package.js
index 11a59c45c6..433a20a42d 100644
--- a/packages/localstorage/package.js
+++ b/packages/localstorage/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Simulates local storage on IE 6,7 using userData",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/logging/package.js b/packages/logging/package.js
index 74b1bc372a..733648a952 100644
--- a/packages/logging/package.js
+++ b/packages/logging/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Logging facility.",
- version: '1.0.8-plugins.1'
+ version: '1.0.8'
});
Npm.depends({
diff --git a/packages/logic-solver/package.js b/packages/logic-solver/package.js
index 2110d8d550..83adec9282 100644
--- a/packages/logic-solver/package.js
+++ b/packages/logic-solver/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "General satisfiability solver for logic problems",
- version: '2.0.0-plugins.0'
+ version: '2.0.1'
});
Package.on_use(function (api) {
diff --git a/packages/markdown/package.js b/packages/markdown/package.js
index 40716e53a6..2c17ac0f5d 100644
--- a/packages/markdown/package.js
+++ b/packages/markdown/package.js
@@ -2,7 +2,7 @@
Package.describe({
summary: "Markdown-to-HTML processor",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function (api) {
diff --git a/packages/meetup/meetup_client.js b/packages/meetup/meetup_client.js
index 2739e4d55d..dc2278ed8d 100644
--- a/packages/meetup/meetup_client.js
+++ b/packages/meetup/meetup_client.js
@@ -35,7 +35,7 @@ Meetup.requestCredential = function (options, credentialRequestCompleteCallback)
'&response_type=code' +
'&scope=' + flatScope +
'&redirect_uri=' + OAuth._redirectUri('meetup', config) +
- '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl);
+ '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);
// meetup box gets taller when permissions requested.
var height = 620;
diff --git a/packages/meetup/package.js b/packages/meetup/package.js
index 25a35d14a9..c33f53fc93 100644
--- a/packages/meetup/package.js
+++ b/packages/meetup/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meetup OAuth flow",
- version: "1.1.4-plugins.0"
+ version: "1.6.0"
});
Package.onUse(function(api) {
diff --git a/packages/meteor-base/package.js b/packages/meteor-base/package.js
index 3a8d31bf13..46bfc3611d 100644
--- a/packages/meteor-base/package.js
+++ b/packages/meteor-base/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'meteor-base',
- version: '1.0.0',
+ version: '1.0.1',
// Brief, one-line summary of the package.
summary: 'Packages that every Meteor app needs',
// By default, Meteor will default to using README.md for documentation.
diff --git a/packages/meteor-developer/meteor_developer_client.js b/packages/meteor-developer/meteor_developer_client.js
index 34a407d1f3..7d55cb85f9 100644
--- a/packages/meteor-developer/meteor_developer_client.js
+++ b/packages/meteor-developer/meteor_developer_client.js
@@ -25,7 +25,7 @@ var requestCredential = function (options, credentialRequestCompleteCallback) {
var loginUrl =
MeteorDeveloperAccounts._server +
"/oauth2/authorize?" +
- "state=" + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl) +
+ "state=" + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl) +
"&response_type=code&" +
"client_id=" + config.clientId;
diff --git a/packages/meteor-developer/package.js b/packages/meteor-developer/package.js
index 6800591b6a..f82a0de2d3 100644
--- a/packages/meteor-developer/package.js
+++ b/packages/meteor-developer/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor developer accounts OAuth flow",
- version: "1.1.4-plugins.0"
+ version: "1.1.4"
});
Package.onUse(function (api) {
diff --git a/packages/meteor-platform/package.js b/packages/meteor-platform/package.js
index ce6646af49..19db5d2eeb 100644
--- a/packages/meteor-platform/package.js
+++ b/packages/meteor-platform/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "(Deprecated) Include a standard set of Meteor packages in your app",
- version: '1.2.3-plugins.0'
+ version: '1.2.3'
});
Package.onUse(function(api) {
diff --git a/packages/meteor-tool/.versions b/packages/meteor-tool/.versions
deleted file mode 100644
index fb6b0f3d87..0000000000
--- a/packages/meteor-tool/.versions
+++ /dev/null
@@ -1,3 +0,0 @@
-meteor@1.1.6
-meteor-tool@1.1.4-pre.2
-underscore@1.0.3
diff --git a/packages/meteor-tool/package.js b/packages/meteor-tool/package.js
index 100c203855..dacd795871 100644
--- a/packages/meteor-tool/package.js
+++ b/packages/meteor-tool/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "The Meteor command-line tool",
- version: '1.1.5-plugins.3'
+ version: '1.1.7'
});
Package.includeTool();
diff --git a/packages/meteor/client_environment.js b/packages/meteor/client_environment.js
index 4e894e0e0f..9f8be953b5 100644
--- a/packages/meteor/client_environment.js
+++ b/packages/meteor/client_environment.js
@@ -25,7 +25,7 @@ Meteor = {
if (typeof __meteor_runtime_config__ === 'object' &&
__meteor_runtime_config__.PUBLIC_SETTINGS) {
/**
- * @summary `Meteor.settings` contains deployment-specific configuration options. You can initialize settings by passing the `--settings` option (which takes the name of a file containing JSON data) to `meteor run` or `meteor deploy`. When running your server directly (e.g. from a bundle), you instead specify settings by putting the JSON directly into the `METEOR_SETTINGS` environment variable. If you don't provide any settings, `Meteor.settings` will be an empty object. If the settings object contains a key named `public`, then `Meteor.settings.public` will be available on the client as well as the server. All other properties of `Meteor.settings` are only defined on the server.
+ * @summary `Meteor.settings` contains deployment-specific configuration options. You can initialize settings by passing the `--settings` option (which takes the name of a file containing JSON data) to `meteor run` or `meteor deploy`. When running your server directly (e.g. from a bundle), you instead specify settings by putting the JSON directly into the `METEOR_SETTINGS` environment variable. If the settings object contains a key named `public`, then `Meteor.settings.public` will be available on the client as well as the server. All other properties of `Meteor.settings` are only defined on the server. You can rely on `Meteor.settings` and `Meteor.settings.public` being defined objects (not undefined) on both client and server even if there are no settings specified. Changes to `Meteor.settings.public` at runtime will be picked up by new client connections.
* @locus Anywhere
* @type {Object}
*/
diff --git a/packages/meteor/package.js b/packages/meteor/package.js
index d3f0cccd8e..8fcdb9e973 100644
--- a/packages/meteor/package.js
+++ b/packages/meteor/package.js
@@ -2,7 +2,7 @@
Package.describe({
summary: "Core Meteor environment",
- version: '1.1.7-plugins.1'
+ version: '1.1.7'
});
Package.registerBuildPlugin({
diff --git a/packages/meteor/server_environment.js b/packages/meteor/server_environment.js
index a5e186182c..b0c7cbb5bf 100644
--- a/packages/meteor/server_environment.js
+++ b/packages/meteor/server_environment.js
@@ -20,7 +20,11 @@ if (! Meteor.settings.public) {
Meteor.settings.public = {};
}
-// Push a subset of settings to the client.
+// Push a subset of settings to the client. Note that the way this
+// code is written, if the app mutates `Meteor.settings.public` on the
+// server, it also mutates
+// `__meteor_runtime_config__.PUBLIC_SETTINGS`, and the modified
+// settings will be sent to the client.
if (typeof __meteor_runtime_config__ === "object") {
__meteor_runtime_config__.PUBLIC_SETTINGS = Meteor.settings.public;
}
diff --git a/packages/meyerweb-reset/package.js b/packages/meyerweb-reset/package.js
index c239e060cf..9802c1d36e 100644
--- a/packages/meyerweb-reset/package.js
+++ b/packages/meyerweb-reset/package.js
@@ -3,7 +3,7 @@
// encourage this pattern. Maybe another solution would be better.
Package.describe({
summary: "reset.css v2.0 from http://meyerweb.com/eric/tools/css/reset/",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/minifiers/package.js b/packages/minifiers/package.js
index 71cb5c90f5..ff77d3a29a 100644
--- a/packages/minifiers/package.js
+++ b/packages/minifiers/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "JavaScript and CSS minifiers",
- version: "1.1.6-plugins.0"
+ version: "1.1.6"
});
Npm.depends({
diff --git a/packages/minimongo/package.js b/packages/minimongo/package.js
index 8e23ae2b44..4e6babac4f 100644
--- a/packages/minimongo/package.js
+++ b/packages/minimongo/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's client-side datastore: a port of MongoDB to Javascript",
- version: '1.0.9-plugins.0'
+ version: '1.0.9'
});
Package.onUse(function (api) {
diff --git a/packages/mobile-experience/package.js b/packages/mobile-experience/package.js
index d44a7840e8..20960dc3c0 100644
--- a/packages/mobile-experience/package.js
+++ b/packages/mobile-experience/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'mobile-experience',
- version: '1.0.0',
+ version: '1.0.1',
// Brief, one-line summary of the package.
summary: 'Packages for a great mobile user experience',
// By default, Meteor will default to using README.md for documentation.
diff --git a/packages/mobile-status-bar/mobile-status-bar.js b/packages/mobile-status-bar/mobile-status-bar.js
deleted file mode 100644
index 2386927459..0000000000
--- a/packages/mobile-status-bar/mobile-status-bar.js
+++ /dev/null
@@ -1,7 +0,0 @@
-if (window.StatusBar) {
- window.StatusBar.overlaysWebView(false);
- window.StatusBar.show();
- window.StatusBar.styleDefault();
- window.StatusBar.backgroundColorByName('white');
-}
-
diff --git a/packages/mobile-status-bar/package.js b/packages/mobile-status-bar/package.js
index 6a46613618..2018a2c8cc 100644
--- a/packages/mobile-status-bar/package.js
+++ b/packages/mobile-status-bar/package.js
@@ -1,10 +1,6 @@
Package.describe({
summary: "Good defaults for the mobile status bar",
- version: "1.0.4-plugins.1"
-});
-
-Package.onUse(function(api) {
- api.addFiles('mobile-status-bar.js', 'web.cordova');
+ version: "1.0.6"
});
Cordova.depends({
diff --git a/packages/mongo-id/package.js b/packages/mongo-id/package.js
index 1a1b97021a..6febb110f7 100644
--- a/packages/mongo-id/package.js
+++ b/packages/mongo-id/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "JS simulation of MongoDB ObjectIDs",
- version: '1.0.0-plugins.0',
+ version: '1.0.1',
documentation: null
});
@@ -11,4 +11,3 @@ Package.onUse(function (api) {
'id.js'
]);
});
-
diff --git a/packages/mongo-livedata/package.js b/packages/mongo-livedata/package.js
index b7a5103594..cc11a581fa 100644
--- a/packages/mongo-livedata/package.js
+++ b/packages/mongo-livedata/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Moved to the 'mongo' package",
- version: '1.0.9-plugins.0'
+ version: '1.0.9'
});
Package.onUse(function (api) {
diff --git a/packages/mongo/allow_tests.js b/packages/mongo/allow_tests.js
index 0f21fc670d..caec455574 100644
--- a/packages/mongo/allow_tests.js
+++ b/packages/mongo/allow_tests.js
@@ -179,12 +179,12 @@ if (Meteor.isServer) {
update: function(userId, doc) {
// throw fields in doc so that we can inspect them in test
throw new Meteor.Error(
- 999, "Test: Fields in doc: " + _.keys(doc).join(','));
+ 999, "Test: Fields in doc: " + _.keys(doc).sort().join(','));
},
remove: function(userId, doc) {
// throw fields in doc so that we can inspect them in test
throw new Meteor.Error(
- 999, "Test: Fields in doc: " + _.keys(doc).join(','));
+ 999, "Test: Fields in doc: " + _.keys(doc).sort().join(','));
},
fetch: ['field1']
});
@@ -202,12 +202,12 @@ if (Meteor.isServer) {
update: function(userId, doc) {
// throw fields in doc so that we can inspect them in test
throw new Meteor.Error(
- 999, "Test: Fields in doc: " + _.keys(doc).join(','));
+ 999, "Test: Fields in doc: " + _.keys(doc).sort().join(','));
},
remove: function(userId, doc) {
// throw fields in doc so that we can inspect them in test
throw new Meteor.Error(
- 999, "Test: Fields in doc: " + _.keys(doc).join(','));
+ 999, "Test: Fields in doc: " + _.keys(doc).sort().join(','));
},
fetch: ['field1']
});
diff --git a/packages/mongo/collection.js b/packages/mongo/collection.js
index 13879a8048..8e0b822838 100644
--- a/packages/mongo/collection.js
+++ b/packages/mongo/collection.js
@@ -678,7 +678,7 @@ Mongo.Collection.prototype.rawDatabase = function () {
* @summary Create a Mongo-style `ObjectID`. If you don't specify a `hexString`, the `ObjectID` will generated randomly (not using MongoDB's ID construction rules).
* @locus Anywhere
* @class
- * @param {String} hexString Optional. The 24-character hexadecimal contents of the ObjectID to create
+ * @param {String} [hexString] Optional. The 24-character hexadecimal contents of the ObjectID to create
*/
Mongo.ObjectID = MongoID.ObjectID;
diff --git a/packages/mongo/package.js b/packages/mongo/package.js
index 900e4bcdf9..9c07aeda61 100644
--- a/packages/mongo/package.js
+++ b/packages/mongo/package.js
@@ -9,7 +9,7 @@
Package.describe({
summary: "Adaptor for using MongoDB and Minimongo over DDP",
- version: '1.1.1-plugins.0'
+ version: '1.1.1'
});
Npm.depends({
diff --git a/packages/oauth-encryption/package.js b/packages/oauth-encryption/package.js
index 8062e7bd95..13f000207d 100644
--- a/packages/oauth-encryption/package.js
+++ b/packages/oauth-encryption/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Encrypt account secrets stored in the database",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/oauth/package.js b/packages/oauth/package.js
index af6c2178e3..69371412bc 100644
--- a/packages/oauth/package.js
+++ b/packages/oauth/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for OAuth-based services",
- version: "1.1.5-plugins.1"
+ version: "1.1.6"
});
Package.onUse(function (api) {
@@ -30,8 +30,10 @@ Package.onUse(function (api) {
api.addFiles('oauth_server.js', 'server');
api.addFiles('pending_credentials.js', 'server');
- api.addFiles('end_of_popup_response.html', 'server', { isAsset: true });
- api.addFiles('end_of_redirect_response.html', 'server', {isAsset: true});
+ api.addAssets([
+ 'end_of_popup_response.html',
+ 'end_of_redirect_response.html'
+ ], 'server');
api.addFiles('oauth_common.js');
diff --git a/packages/oauth1/package.js b/packages/oauth1/package.js
index 2a8d5a033e..eb20d9a922 100644
--- a/packages/oauth1/package.js
+++ b/packages/oauth1/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for OAuth1-based login services",
- version: "1.1.5-plugins.0"
+ version: "1.1.5"
});
Package.onUse(function (api) {
diff --git a/packages/oauth2/package.js b/packages/oauth2/package.js
index a5eedb7191..e4a768b8f1 100644
--- a/packages/oauth2/package.js
+++ b/packages/oauth2/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for OAuth2-based login services",
- version: "1.1.4-plugins.0"
+ version: "1.1.4"
});
Package.onUse(function (api) {
diff --git a/packages/observe-sequence/package.js b/packages/observe-sequence/package.js
index 8d2d1a85f3..85a7b381fd 100644
--- a/packages/observe-sequence/package.js
+++ b/packages/observe-sequence/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Observe changes to various sequence types such as arrays, cursors and objects",
- version: "1.0.7-plugins.0"
+ version: "1.0.7"
});
Package.onUse(function (api) {
diff --git a/packages/ordered-dict/package.js b/packages/ordered-dict/package.js
index c84f2f7428..19861d7b80 100644
--- a/packages/ordered-dict/package.js
+++ b/packages/ordered-dict/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Ordered traversable dictionary with a mutable ordering",
- version: '1.0.4-plugins.0',
+ version: '1.0.4',
documentation: null
});
diff --git a/packages/package-stats-opt-out/package.js b/packages/package-stats-opt-out/package.js
index eebd20f317..f5c7d8363d 100644
--- a/packages/package-stats-opt-out/package.js
+++ b/packages/package-stats-opt-out/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Opt out of sending package stats",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/package-version-parser/package.js b/packages/package-version-parser/package.js
index 5c3a54aa92..b3a1dcf955 100644
--- a/packages/package-version-parser/package.js
+++ b/packages/package-version-parser/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Parses Meteor Smart Package version strings",
- version: "3.0.4-plugins.0"
+ version: "3.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/preserve-inputs/package.js b/packages/preserve-inputs/package.js
index 3abf40227e..c2eb3f0b3e 100644
--- a/packages/preserve-inputs/package.js
+++ b/packages/preserve-inputs/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Deprecated package (now empty)",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/promise/package.js b/packages/promise/package.js
index be33d8f7b9..52e9a47951 100644
--- a/packages/promise/package.js
+++ b/packages/promise/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: "promise",
- version: "0.4.8-plugins.0",
+ version: "0.4.8",
summary: "ECMAScript 2015 Promise polyfill with Fiber support",
git: "https://github.com/meteor/promise",
documentation: "README.md"
diff --git a/packages/random/package.js b/packages/random/package.js
index ec63393cdd..d11df4b117 100644
--- a/packages/random/package.js
+++ b/packages/random/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Random number generator and utilities",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/rate-limit/rate-limit.js b/packages/rate-limit/rate-limit.js
index 9324b00af3..69cbd2978b 100644
--- a/packages/rate-limit/rate-limit.js
+++ b/packages/rate-limit/rate-limit.js
@@ -112,7 +112,7 @@ RateLimiter = function () {
// id. Each rule object stores the rule pattern, number of events allowed,
// last reset time and the rule reset interval in milliseconds.
self.rules = {};
-}
+};
/**
* Checks if this input has exceeded any rate limits.
@@ -172,7 +172,7 @@ RateLimiter.prototype.check = function (input) {
}
});
return reply;
-}
+};
/**
* Adds a rule to dictionary of rules that are checked against on every call.
@@ -196,12 +196,12 @@ RateLimiter.prototype.addRule = function (rule, numRequestsAllowed,
var options = {
numRequestsAllowed: numRequestsAllowed || DEFAULT_REQUESTS_PER_INTERVAL,
intervalTime: intervalTime || DEFAULT_INTERVAL_TIME_IN_MILLISECONDS
- }
+ };
var newRule = new Rule(options, rule);
this.rules[newRule.id] = newRule;
return newRule.id;
-}
+};
/**
* Increment counters in every rule that match to this input
@@ -228,7 +228,7 @@ RateLimiter.prototype.increment = function (input) {
else
rule.counters[ruleResult.key] = 1;
});
-}
+};
// Returns an array of all rules that apply to provided input
RateLimiter.prototype._findAllMatchingRules = function (input) {
@@ -237,7 +237,7 @@ RateLimiter.prototype._findAllMatchingRules = function (input) {
return _.filter(self.rules, function(rule) {
return rule.match(input);
});
-}
+};
/**
* Provides a mechanism to remove rules from the rate limiter. Returns boolean
* about success.
@@ -252,4 +252,4 @@ RateLimiter.prototype.removeRule = function (id) {
} else {
return false;
}
-}
\ No newline at end of file
+};
diff --git a/packages/reactive-dict/package.js b/packages/reactive-dict/package.js
index 2a5cbc33ac..a83e2c0ec4 100644
--- a/packages/reactive-dict/package.js
+++ b/packages/reactive-dict/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Reactive dictionary",
- version: '1.1.1-plugins.0'
+ version: '1.1.1'
});
Package.onUse(function (api) {
diff --git a/packages/reactive-var/package.js b/packages/reactive-var/package.js
index 7e952cd389..33649820c4 100644
--- a/packages/reactive-var/package.js
+++ b/packages/reactive-var/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Reactive variable",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/reload-safetybelt/package.js b/packages/reload-safetybelt/package.js
index 765da1600d..6379861327 100644
--- a/packages/reload-safetybelt/package.js
+++ b/packages/reload-safetybelt/package.js
@@ -1,16 +1,16 @@
Package.describe({
summary: "Reload safety belt for multi-server deployments",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
api.use("webapp", "server");
api.addFiles("reload-safety-belt.js", "server");
- api.addFiles("safetybelt.js", "server", { isAsset: true });
+ api.addAssets("safetybelt.js", "server");
});
Package.onTest(function (api) {
- api.addFiles("safetybelt.js", "server", { isAsset: true });
+ api.addAssets("safetybelt.js", "server");
api.use(["reload-safetybelt", "tinytest", "http", "webapp", "underscore"]);
api.addFiles("reload-safety-belt-tests.js", "server");
});
diff --git a/packages/reload/package.js b/packages/reload/package.js
index 5117656937..450b7235c3 100644
--- a/packages/reload/package.js
+++ b/packages/reload/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Reload the page while preserving application state.",
- version: '1.1.4-plugins.0'
+ version: '1.1.4'
});
Package.onUse(function (api) {
diff --git a/packages/retry/package.js b/packages/retry/package.js
index 0d64378e7d..2060a27a0f 100644
--- a/packages/retry/package.js
+++ b/packages/retry/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Retry logic with exponential backoff",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/routepolicy/package.js b/packages/routepolicy/package.js
index ef4cddbaeb..15a6b91fc0 100644
--- a/packages/routepolicy/package.js
+++ b/packages/routepolicy/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "route policy declarations",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/service-configuration/package.js b/packages/service-configuration/package.js
index dfb18a38df..c19ca7af54 100644
--- a/packages/service-configuration/package.js
+++ b/packages/service-configuration/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Manage the configuration for third-party services",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/session/package.js b/packages/session/package.js
index 4e4eb8eefa..87d284b580 100644
--- a/packages/session/package.js
+++ b/packages/session/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Session variable",
- version: '1.1.1-plugins.0'
+ version: '1.1.1'
});
Package.onUse(function (api) {
diff --git a/packages/sha/package.js b/packages/sha/package.js
index bbcff1fde9..389af07878 100644
--- a/packages/sha/package.js
+++ b/packages/sha/package.js
@@ -1,5 +1,5 @@
Package.describe({
- version: '1.0.4-plugins.0',
+ version: '1.0.4',
summary: 'SHA256 implementation',
git: 'https://github.com/meteor/meteor'
});
diff --git a/packages/showdown/package.js b/packages/showdown/package.js
index 14098eff49..85d3bd34ff 100644
--- a/packages/showdown/package.js
+++ b/packages/showdown/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Moved to the 'markdown' package",
- version: '1.0.5-plugins.0'
+ version: '1.0.5'
});
Package.onUse(function (api) {
diff --git a/packages/spacebars-compiler/codegen.js b/packages/spacebars-compiler/codegen.js
index f81621803b..3147a4a18a 100644
--- a/packages/spacebars-compiler/codegen.js
+++ b/packages/spacebars-compiler/codegen.js
@@ -111,8 +111,8 @@ _.extend(CodeGen.prototype, {
var eachUsage = "Use either {{#each items}} or " +
"{{#each item in items}} form of #each.";
var inArg = args[1];
- if (! (args.length === 3 && inArg[1].length === 1)) {
- // we don't have 3 space-separated parts after #each, or
+ if (! (args.length >= 3 && inArg[1].length === 1)) {
+ // we don't have at least 3 space-separated parts after #each, or
// inArg doesn't look like ['PATH',['in']]
throw new Error("Malformed #each. " + eachUsage);
}
@@ -123,9 +123,9 @@ _.extend(CodeGen.prototype, {
throw new Error("Bad variable name in #each");
}
var variable = variableArg[1][0];
- dataCode = 'function () { return { _sequence: Spacebars.call(' +
- self.codeGenPath(args[2][1]) +
- '), _variable: ' + BlazeTools.toJSLiteral(variable) + ' }; }';
+ dataCode = 'function () { return { _sequence: ' +
+ self.codeGenInclusionData(args.slice(2)) +
+ ', _variable: ' + BlazeTools.toJSLiteral(variable) + ' }; }';
} else if (path[0] === 'let') {
var dataProps = {};
_.each(args, function (arg) {
@@ -344,11 +344,9 @@ _.extend(CodeGen.prototype, {
return SpacebarsCompiler.codeGen(content);
},
- codeGenInclusionDataFunc: function (args) {
+ codeGenInclusionData: function (args) {
var self = this;
- var dataFuncCode = null;
-
if (! args.length) {
// e.g. `{{#foo}}`
return null;
@@ -359,24 +357,33 @@ _.extend(CodeGen.prototype, {
var argKey = arg[2];
dataProps[argKey] = 'Spacebars.call(' + self.codeGenArgValue(arg) + ')';
});
- dataFuncCode = makeObjectLiteral(dataProps);
+ return makeObjectLiteral(dataProps);
} else if (args[0][0] !== 'PATH') {
// literal first argument, e.g. `{{> foo "blah"}}`
//
// tag validation has confirmed, in this case, that there is only
// one argument (`args.length === 1`)
- dataFuncCode = self.codeGenArgValue(args[0]);
+ return self.codeGenArgValue(args[0]);
} else if (args.length === 1) {
// one argument, must be a PATH
- dataFuncCode = 'Spacebars.call(' + self.codeGenPath(args[0][1]) + ')';
+ return 'Spacebars.call(' + self.codeGenPath(args[0][1]) + ')';
} else {
// Multiple positional arguments; treat them as a nested
// "data mustache"
- dataFuncCode = self.codeGenMustache(args[0][1], args.slice(1),
- 'dataMustache');
+ return self.codeGenMustache(args[0][1], args.slice(1),
+ 'dataMustache');
}
- return 'function () { return ' + dataFuncCode + '; }';
+ },
+
+ codeGenInclusionDataFunc: function (args) {
+ var self = this;
+ var dataCode = self.codeGenInclusionData(args);
+ if (dataCode) {
+ return 'function () { return ' + dataCode + '; }';
+ } else {
+ return null;
+ }
}
});
diff --git a/packages/spacebars-compiler/package.js b/packages/spacebars-compiler/package.js
index 5cd1b0a007..7a62f9deba 100644
--- a/packages/spacebars-compiler/package.js
+++ b/packages/spacebars-compiler/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Compiler for Spacebars template language",
- version: '1.0.7-plugins.1'
+ version: '1.0.7'
});
Package.onUse(function (api) {
diff --git a/packages/spacebars-tests/package.js b/packages/spacebars-tests/package.js
index ecd9bcaa2e..644d24cc69 100644
--- a/packages/spacebars-tests/package.js
+++ b/packages/spacebars-tests/package.js
@@ -1,12 +1,13 @@
Package.describe({
summary: "Additional tests for Spacebars",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
// These tests are in a separate package to avoid a circular dependency
// between the `spacebars` and `templating` packages.
Package.onTest(function (api) {
api.use([
+ 'es5-shim',
'underscore',
'spacebars',
'tinytest',
@@ -35,11 +36,11 @@ Package.onTest(function (api) {
api.addFiles('template_tests_server.js', 'server');
- api.addFiles([
+ api.addAssets([
'assets/markdown_basic.html',
'assets/markdown_if1.html',
'assets/markdown_if2.html',
'assets/markdown_each1.html',
'assets/markdown_each2.html'
- ], 'server', { isAsset: true });
+ ], 'server');
});
diff --git a/packages/spacebars-tests/template_tests.html b/packages/spacebars-tests/template_tests.html
index d2253ec9c3..222a458884 100644
--- a/packages/spacebars-tests/template_tests.html
+++ b/packages/spacebars-tests/template_tests.html
@@ -1061,6 +1061,14 @@ Hi there!
{{/with}}
+
+ {{#with dataContext}}
+ {{#each item in (items)}}
+
{{item.text}} -- {{toplevel}}
+ {{/each}}
+ {{/with}}
+
+
{{#with dataContext}}
{{#each item in letter_a}}
@@ -1140,3 +1148,9 @@ Hi there!
{{@index}}
+
+
+ {{#each item in helper list}}
+
{{item}}
+ {{/each}}
+
diff --git a/packages/spacebars-tests/template_tests.js b/packages/spacebars-tests/template_tests.js
index fcaaaddce9..2d3a514b5a 100644
--- a/packages/spacebars-tests/template_tests.js
+++ b/packages/spacebars-tests/template_tests.js
@@ -1428,7 +1428,7 @@ _.each(['textarea', 'text', 'password', 'submit', 'button',
},
type: type
});
- };
+ }
var div = renderToDiv(tmpl);
document.body.appendChild(div);
@@ -3225,6 +3225,25 @@ Tinytest.add("spacebars-tests - template_tests - new #each extends data context"
Blaze.remove(view);
});
+// Same as above, but now the argument to each in has a subexpression
+Tinytest.add("spacebars-tests - template_tests - new #each with subexpression (#5137)", function (test) {
+ var tmpl = Template.spacebars_template_test_new_each_data_context_subexpr;
+ tmpl.helpers({
+ dataContext: function () {
+ return {
+ items: [{text:"a"}, {text:"b"}],
+ toplevel: "XYZ"
+ };
+ }
+ });
+
+ var div = document.createElement("DIV");
+ var theWith = Blaze.render(tmpl, div);
+ test.equal(canonicalizeHtml(div.innerHTML), '
a -- XYZ
b -- XYZ
');
+ var view = Blaze.getView(div.querySelector('div'));
+ Blaze.remove(view);
+});
+
Tinytest.add("spacebars-tests - template_tests - new #each binding lookup is scoped to the template", function (test) {
var tmpl = Template.spacebars_template_test_new_each_lookup_top_level;
tmpl.helpers({
@@ -3429,3 +3448,17 @@ Tinytest.add("spacebars-tests - template_tests - lexical scope doesn't leak", fu
var div = renderToDiv(tmpl);
}, /Unsupported directive/);
});
+
+// PR #5138
+Tinytest.add("spacebars-tests - template_tests - multiple arguments in each-in", function (test) {
+ var tmpl = Template.spacebars_template_test_each_in_multi_args;
+ tmpl.helpers({
+ list: ['a', 'b', 'c'],
+ helper: function (list) {
+ return list.reverse();
+ }
+ });
+
+ var div = renderToDiv(tmpl);
+ test.equal(canonicalizeHtml(div.innerHTML), "
c
b
a
");
+});
diff --git a/packages/spacebars/package.js b/packages/spacebars/package.js
index 0ff80091ea..296c9f4218 100644
--- a/packages/spacebars/package.js
+++ b/packages/spacebars/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Handlebars-like template language for Meteor",
- version: '1.0.7-plugins.0'
+ version: '1.0.7'
});
// For more, see package `spacebars-compiler`, which is used by
diff --git a/packages/spiderable/package.js b/packages/spiderable/package.js
index 9939dd4556..4f6f92aed6 100644
--- a/packages/spiderable/package.js
+++ b/packages/spiderable/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Makes the application crawlable to web spiders",
- version: "1.0.8-plugins.0"
+ version: "1.0.9"
});
Package.onUse(function (api) {
@@ -17,7 +17,7 @@ Package.onUse(function (api) {
api.addFiles('spiderable_server.js', 'server');
api.addFiles('spiderable_client.js', 'client');
- api.addFiles('phantom_script.js', 'server', { isAsset: true });
+ api.addAssets('phantom_script.js', 'server');
});
Package.onTest(function (api) {
diff --git a/packages/srp/package.js b/packages/srp/package.js
index 01efa389f6..5149ccde3c 100644
--- a/packages/srp/package.js
+++ b/packages/srp/package.js
@@ -5,7 +5,7 @@
Package.describe({
summary: "Library for Secure Remote Password (SRP) exchanges",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/standard-app-packages/package.js b/packages/standard-app-packages/package.js
index 8bcdc2fc96..54f82ee797 100644
--- a/packages/standard-app-packages/package.js
+++ b/packages/standard-app-packages/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Moved to meteor-platform",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/startup/package.js b/packages/startup/package.js
index aa79f13002..4b69b96638 100644
--- a/packages/startup/package.js
+++ b/packages/startup/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Deprecated package (now empty)",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function (api) {
diff --git a/packages/stylus/.npm/plugin/compileStylusBatch/README b/packages/stylus/.npm/plugin/compileStylusBatch/README
index 8310d7d486..3d492553a4 100644
--- a/packages/stylus/.npm/plugin/compileStylusBatch/README
+++ b/packages/stylus/.npm/plugin/compileStylusBatch/README
@@ -1,7 +1,7 @@
-This directory and the files immediately inside it are automatically generated
-when you change this package's NPM dependencies. Commit the files in this
-directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
-so that others run the same versions of sub-dependencies.
-
-You should NOT check in the node_modules directory that Meteor automatically
+This directory and the files immediately inside it are automatically generated
+when you change this package's NPM dependencies. Commit the files in this
+directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
+so that others run the same versions of sub-dependencies.
+
+You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.
diff --git a/packages/stylus/.npm/plugin/compileStylusBatch/npm-shrinkwrap.json b/packages/stylus/.npm/plugin/compileStylusBatch/npm-shrinkwrap.json
index 00c1381671..9bfa1ac2c7 100644
--- a/packages/stylus/.npm/plugin/compileStylusBatch/npm-shrinkwrap.json
+++ b/packages/stylus/.npm/plugin/compileStylusBatch/npm-shrinkwrap.json
@@ -1,112 +1,112 @@
-{
- "dependencies": {
- "nib": {
- "version": "1.1.0",
- "dependencies": {
- "stylus": {
- "version": "0.49.3",
- "dependencies": {
- "css-parse": {
- "version": "1.7.0"
- },
- "mkdirp": {
- "version": "0.3.5"
- },
- "debug": {
- "version": "2.2.0",
- "dependencies": {
- "ms": {
- "version": "0.7.1"
- }
- }
- },
- "sax": {
- "version": "0.5.8"
- },
- "glob": {
- "version": "3.2.11",
- "dependencies": {
- "inherits": {
- "version": "2.0.1"
- },
- "minimatch": {
- "version": "0.3.0",
- "dependencies": {
- "lru-cache": {
- "version": "2.6.5"
- },
- "sigmund": {
- "version": "1.0.1"
- }
- }
- }
- }
- },
- "source-map": {
- "version": "0.1.43",
- "dependencies": {
- "amdefine": {
- "version": "0.1.1"
- }
- }
- }
- }
- }
- }
- },
- "stylus": {
- "version": "https://github.com/meteor/stylus/tarball/d4352c9cb4056faf238e6bd9f9f2172472b67c5b",
- "dependencies": {
- "css-parse": {
- "version": "1.7.0"
- },
- "mkdirp": {
- "version": "0.5.1",
- "dependencies": {
- "minimist": {
- "version": "0.0.8"
- }
- }
- },
- "debug": {
- "version": "2.2.0",
- "dependencies": {
- "ms": {
- "version": "0.7.1"
- }
- }
- },
- "sax": {
- "version": "0.5.8"
- },
- "glob": {
- "version": "3.2.11",
- "dependencies": {
- "inherits": {
- "version": "2.0.1"
- },
- "minimatch": {
- "version": "0.3.0",
- "dependencies": {
- "lru-cache": {
- "version": "2.6.5"
- },
- "sigmund": {
- "version": "1.0.1"
- }
- }
- }
- }
- },
- "source-map": {
- "version": "0.1.43",
- "dependencies": {
- "amdefine": {
- "version": "1.0.0"
- }
- }
- }
- }
- }
- }
+{
+ "dependencies": {
+ "nib": {
+ "version": "1.1.0",
+ "dependencies": {
+ "stylus": {
+ "version": "0.49.3",
+ "dependencies": {
+ "css-parse": {
+ "version": "1.7.0"
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ },
+ "debug": {
+ "version": "2.2.0",
+ "dependencies": {
+ "ms": {
+ "version": "0.7.1"
+ }
+ }
+ },
+ "sax": {
+ "version": "0.5.8"
+ },
+ "glob": {
+ "version": "3.2.11",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.5"
+ },
+ "sigmund": {
+ "version": "1.0.1"
+ }
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "dependencies": {
+ "amdefine": {
+ "version": "0.1.1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "stylus": {
+ "version": "https://github.com/meteor/stylus/tarball/d4352c9cb4056faf238e6bd9f9f2172472b67c5b",
+ "dependencies": {
+ "css-parse": {
+ "version": "1.7.0"
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "debug": {
+ "version": "2.2.0",
+ "dependencies": {
+ "ms": {
+ "version": "0.7.1"
+ }
+ }
+ },
+ "sax": {
+ "version": "0.5.8"
+ },
+ "glob": {
+ "version": "3.2.11",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.5"
+ },
+ "sigmund": {
+ "version": "1.0.1"
+ }
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "dependencies": {
+ "amdefine": {
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/packages/stylus/package.js b/packages/stylus/package.js
index 1c20b7d4cf..6cdc5603fc 100644
--- a/packages/stylus/package.js
+++ b/packages/stylus/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: 'Expressive, dynamic, robust CSS',
- version: "2.511.0-plugins.1"
+ version: "2.511.0"
});
Package.registerBuildPlugin({
diff --git a/packages/templating/package.js b/packages/templating/package.js
index 6118d77cc3..2cd3c93cfb 100644
--- a/packages/templating/package.js
+++ b/packages/templating/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Allows templates to be defined in .html files",
- version: '1.1.2-plugins.1'
+ version: '1.1.2'
});
// Today, this package is closely intertwined with Handlebars, meaning
diff --git a/packages/test-helpers/package.js b/packages/test-helpers/package.js
index 534d14fa37..aa8950040a 100644
--- a/packages/test-helpers/package.js
+++ b/packages/test-helpers/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Utility functions for tests",
- version: '1.0.5-plugins.0'
+ version: '1.0.5'
});
Package.onUse(function (api) {
diff --git a/packages/test-in-browser/package.js b/packages/test-in-browser/package.js
index 2896bdc404..98e674e879 100644
--- a/packages/test-in-browser/package.js
+++ b/packages/test-in-browser/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Run tests interactively in the browser",
- version: '1.0.8-plugins.1',
+ version: '1.0.8',
documentation: null
});
diff --git a/packages/test-in-console/package.js b/packages/test-in-console/package.js
index fab8a0a978..b5d39e27c6 100644
--- a/packages/test-in-console/package.js
+++ b/packages/test-in-console/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Run tests noninteractively, with results going to the console.",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
@@ -14,5 +14,5 @@ Package.onUse(function (api) {
api.addFiles(['reporter.js'], "server");
// This is to be run by phantomjs, not as part of normal package code.
- api.addFiles('runner.js', 'server', {isAsset: true});
+ api.addAssets('runner.js', 'server');
});
diff --git a/packages/test-in-console/run.sh b/packages/test-in-console/run.sh
index 2b31a7a065..9eb2b96428 100755
--- a/packages/test-in-console/run.sh
+++ b/packages/test-in-console/run.sh
@@ -10,13 +10,12 @@ export PATH=$METEOR_HOME:$PATH
export URL='http://localhost:4096/'
-meteor test-packages --driver-package test-in-console -p 4096 &
-METEOR_PID=$!
-
-sleep 2
+exec 3< <(meteor test-packages --driver-package test-in-console -p 4096 --exclude $TEST_PACKAGES_EXCLUDE)
+EXEC_PID=$!
+sed '/test-in-console listening$/q' <&3
phantomjs $METEOR_HOME/packages/test-in-console/runner.js
STATUS=$?
-kill $METEOR_PID
+pkill -TERM -P $EXEC_PID
exit $STATUS
diff --git a/packages/test-server-tests-in-console-once/package.js b/packages/test-server-tests-in-console-once/package.js
index 46d24ab0af..2b40ee35f9 100644
--- a/packages/test-server-tests-in-console-once/package.js
+++ b/packages/test-server-tests-in-console-once/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Run server tests noninteractively, with results going to the console.",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/tinytest/package.js b/packages/tinytest/package.js
index 0852d9d3f7..46934374df 100644
--- a/packages/tinytest/package.js
+++ b/packages/tinytest/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Tiny testing framework",
- version: '1.0.6-plugins.0'
+ version: '1.0.6'
});
Package.onUse(function (api) {
diff --git a/packages/tracker/package.js b/packages/tracker/package.js
index fb2dacc817..7f33529b40 100644
--- a/packages/tracker/package.js
+++ b/packages/tracker/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Dependency tracker to allow reactive callbacks",
- version: '1.0.8-plugins.0'
+ version: '1.0.8'
});
Package.onUse(function (api) {
diff --git a/packages/twitter/package.js b/packages/twitter/package.js
index 5edde07d20..f383c9e534 100644
--- a/packages/twitter/package.js
+++ b/packages/twitter/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Twitter OAuth flow",
- version: '1.1.5-plugins.0'
+ version: '1.1.5'
});
Package.onUse(function(api) {
diff --git a/packages/twitter/twitter_client.js b/packages/twitter/twitter_client.js
index 1aa4b361df..09c1bcc19d 100644
--- a/packages/twitter/twitter_client.js
+++ b/packages/twitter/twitter_client.js
@@ -29,7 +29,7 @@ Twitter.requestCredential = function (options, credentialRequestCompleteCallback
// url to app, enters "step 1" as described in
// packages/accounts-oauth1-helper/oauth1_server.js
var loginPath = '_oauth/twitter/?requestTokenAndRedirect=true'
- + '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl);
+ + '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);
if (Meteor.isCordova) {
loginPath = loginPath + "&cordova=true";
diff --git a/packages/ui/package.js b/packages/ui/package.js
index 1fd4698849..1c3d1bf39f 100644
--- a/packages/ui/package.js
+++ b/packages/ui/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Deprecated: Use the 'blaze' package",
- version: '1.0.7-plugins.0'
+ version: '1.0.7'
});
Package.onUse(function (api) {
diff --git a/packages/underscore-tests/package.js b/packages/underscore-tests/package.js
index 74ef6851f2..d4ffac599b 100644
--- a/packages/underscore-tests/package.js
+++ b/packages/underscore-tests/package.js
@@ -2,7 +2,7 @@ Package.describe({
// These tests can't be directly in the underscore packages since
// Tinytest depends on underscore
summary: "Tests for the underscore package",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onTest(function (api) {
diff --git a/packages/underscore/package.js b/packages/underscore/package.js
index 455f8ac291..d3f2a55fdd 100644
--- a/packages/underscore/package.js
+++ b/packages/underscore/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Collection of small helpers: _.map, _.each, ...",
- version: '1.0.4-plugins.0'
+ version: '1.0.4'
});
Package.onUse(function (api) {
diff --git a/packages/url/package.js b/packages/url/package.js
index daaf9c220e..fd6435dabf 100644
--- a/packages/url/package.js
+++ b/packages/url/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Utility code for constructing URLs",
- version: "1.0.5-plugins.0"
+ version: "1.0.5"
});
Package.onUse(function(api) {
diff --git a/packages/webapp-hashing/package.js b/packages/webapp-hashing/package.js
index 03a0c36792..b5fb5be714 100644
--- a/packages/webapp-hashing/package.js
+++ b/packages/webapp-hashing/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Used internally by WebApp. Knows how to hash programs from manifests.",
- version: "1.0.4-plugins.0"
+ version: "1.0.4"
});
Package.onUse(function(api) {
diff --git a/packages/webapp/package.js b/packages/webapp/package.js
index 5b880b09af..26e3777087 100644
--- a/packages/webapp/package.js
+++ b/packages/webapp/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Serves a Meteor app over HTTP",
- version: '1.2.1-plugins.1'
+ version: '1.2.2'
});
Npm.depends({connect: "2.9.0",
@@ -17,7 +17,7 @@ Cordova.depends({
'cordova-plugin-legacy-whitelist': '1.1.0',
// the cordova plugin built by Meteor Core team that "emulates a server" on
// the mobile device. Serving the files and checking for the HCP updates.
- 'com.meteor.cordova-update': 'https://github.com/meteor/com.meteor.cordova-update/tarball/92fe99b7248075318f6446b288995d4381d24cd2'
+ 'com.meteor.cordova-update': 'https://github.com/meteor/com.meteor.cordova-update.git#16c53f53e438fc8b1b9c768de36f0a8974e38b49'
});
Package.onUse(function (api) {
diff --git a/packages/weibo/package.js b/packages/weibo/package.js
index ebbc08b5c2..f64ad475e2 100644
--- a/packages/weibo/package.js
+++ b/packages/weibo/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "Weibo OAuth flow",
- version: '1.1.4-plugins.0'
+ version: '1.1.4'
});
Package.onUse(function(api) {
diff --git a/packages/weibo/weibo_client.js b/packages/weibo/weibo_client.js
index ef9c0afc16..b61aff17a4 100644
--- a/packages/weibo/weibo_client.js
+++ b/packages/weibo/weibo_client.js
@@ -29,7 +29,7 @@ Weibo.requestCredential = function (options, credentialRequestCompleteCallback)
'?response_type=code' +
'&client_id=' + config.clientId +
'&redirect_uri=' + OAuth._redirectUri('weibo', config, null, {replaceLocalhost: true}) +
- '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl);
+ '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);
OAuth.launchLogin({
loginService: "weibo",
diff --git a/packages/xmlbuilder/package.js b/packages/xmlbuilder/package.js
index 3b0f1410cf..a723e853c6 100644
--- a/packages/xmlbuilder/package.js
+++ b/packages/xmlbuilder/package.js
@@ -1,6 +1,6 @@
Package.describe({
summary: "An XML builder for node.js similar to java-xmlbuilder.",
- version: '2.4.7-plugins.0'
+ version: '2.4.7'
});
Npm.depends({
diff --git a/scripts/admin/banners.json b/scripts/admin/banners.json
index b6adb901b6..fb5f5d613f 100644
--- a/scripts/admin/banners.json
+++ b/scripts/admin/banners.json
@@ -2,22 +2,12 @@
"banners": [
{
"versions": ["0.9.0", "0.9.0.1", "0.9.1", "0.9.1.1", "0.9.2",
- "0.9.2.1", "0.9.2.2", "0.9.3", "0.9.3.1", "0.9.4", "1.0", "1.0.1", "1.0.2", "1.0.2.1",
- "1.0.3", "1.0.3.1", "1.0.3.2", "1.0.4", "1.0.4.1", "1.0.4.2", "1.0.5"],
+ "0.9.2.1", "0.9.2.2", "0.9.3", "0.9.3.1", "0.9.4", "1.0",
+ "1.0.1", "1.0.2", "1.0.2.1", "1.0.3", "1.0.3.1", "1.0.3.2",
+ "1.0.4", "1.0.4.1", "1.0.4.2", "1.0.5", "1.1", "1.1.0.1",
+ "1.1.0.2", "1.1.0.3"],
"banner": {
- "text": "=> Meteor 1.1.0.3: official Windows support, new package version constraint solver.\n\n\n This release is being downloaded in the background. Update your app to\n Meteor 1.1.0.3 by running 'meteor update'."
- }
- },
- {
- "versions": ["1.1", "1.1.0.1"],
- "banner": {
- "text": "=> Meteor 1.1.0.3: bug fixes to 1.1.\n\n\n This release is being downloaded in the background. Update your app to\n Meteor 1.1.0.3 by running 'meteor update'."
- }
- },
- {
- "versions": ["1.1.0.2"],
- "banner": {
- "text": "=> Meteor 1.1.0.3: fixes Facebook integration for new apps.\n\n\n This release is being downloaded in the background. Update your app to\n Meteor 1.1.0.3 by running 'meteor update'."
+ "text": "=> Meteor 1.2: React and Angular support, ECMAScript 2015, new faster build\n system, and improvements to the Cordova integration for mobile apps.\n\n\n This release is being downloaded in the background. Update your app to\n Meteor 1.2 by running 'meteor update'."
}
}
]
diff --git a/scripts/admin/meteor-release-experimental.json b/scripts/admin/meteor-release-experimental.json
index 445d1ef95a..cc32c41e85 100644
--- a/scripts/admin/meteor-release-experimental.json
+++ b/scripts/admin/meteor-release-experimental.json
@@ -1,7 +1,7 @@
{
- "track": "PLUGINS-PREVIEW",
- "version": "2",
+ "track": "METEOR",
+ "version": "1.2-rc.19",
"recommended": false,
"official": false,
- "description": "Preview release for new plugin APIs"
+ "description": "Preview release of Meteor 1.2"
}
diff --git a/scripts/admin/meteor-release-official.json b/scripts/admin/meteor-release-official.json
index 0c10826a8e..48a2a0e730 100644
--- a/scripts/admin/meteor-release-official.json
+++ b/scripts/admin/meteor-release-official.json
@@ -1,6 +1,6 @@
{
"track": "METEOR",
- "version": "1.1.0.3",
+ "version": "1.2",
"recommended": false,
"official": true,
"description": "The Official Meteor Distribution"
diff --git a/scripts/admin/publish-meteor-tool-on-arch.sh b/scripts/admin/publish-meteor-tool-on-arch.sh
index 0f191bad0f..3b6821674c 100755
--- a/scripts/admin/publish-meteor-tool-on-arch.sh
+++ b/scripts/admin/publish-meteor-tool-on-arch.sh
@@ -78,7 +78,12 @@ END
# delete existing batch script if it exists
ssh $USERNAME@$HOST -oUserKnownHostsFile="$TEMP_KEY" -p "$PORT" -i "$TEMP_PRIV_KEY" "cmd /c del C:\\publish-tool.bat || exit 0" 2>/dev/null
- # copy batch script to windows machine
+ # copy batch script to windows machine, in a funky way, by splicing each
+ # line of publish-meteor-tool.bat into a Windows `echo` command, with no
+ # escaping.
+ # therefore, the lines of publish-meteor-tool.bat must already be
+ # escaped, for example `>` as `^>` and `%` as `^%`.
+
BAT_FILENAME="$ADMIN_DIR/publish-meteor-tool.bat"
# we need to use file descriptor 10 because otherwise SSH will conflict with
@@ -105,6 +110,8 @@ END
}
# get keys from "meteor admin get-machine" command
+# inputs: SESSION_FILE, METEOR, PLATFORM, CHECKOUT_DIR
+# outputs: USERNAME, HOST, PORT, TEMP_KEY, TEMP_PRIV_KEY
parse_keys () {
trap 'echo "${red}Failed to parse the machine credentials${NC}";clean_up' EXIT
CREDS=$(METEOR_SESSION_FILE="$SESSION_FILE" "$METEOR" admin get-machine $PLATFORM --json)
diff --git a/tools/cli/commands-cordova.js b/tools/cli/commands-cordova.js
index e74bddeed7..d3549313e0 100644
--- a/tools/cli/commands-cordova.js
+++ b/tools/cli/commands-cordova.js
@@ -74,8 +74,7 @@ main.registerCommand({
minArgs: 1,
maxArgs: Infinity,
requiresApp: true,
- catalogRefresh: new catalog.Refresh.Never(),
- notOnWindows: true
+ catalogRefresh: new catalog.Refresh.Never()
}, function (options) {
const projectContext = createProjectContext(options.appDir);
@@ -95,20 +94,18 @@ version of Meteor`);
if (buildmessage.jobHasMessages()) return;
- const cordovaProject = new CordovaProject(projectContext);
-
installedPlatforms = _.without(installedPlatforms, ...platformsToRemove);
- const cordovaPlatforms = cordova.filterPlatforms(installedPlatforms);
- cordovaProject.ensurePlatformsAreSynchronized(cordovaPlatforms);
-
- if (buildmessage.jobHasMessages()) return;
-
- // Only write the new platform list when we have succesfully synchronized
projectContext.platformList.write(installedPlatforms);
for (platform of platformsToRemove) {
Console.info(`${platform}: removed platform`);
}
+
+ if (process.platform !== 'win32') {
+ const cordovaProject = new CordovaProject(projectContext);
+ const cordovaPlatforms = cordova.filterPlatforms(installedPlatforms);
+ cordovaProject.ensurePlatformsAreSynchronized(cordovaPlatforms);
+ }
});
});
diff --git a/tools/cli/commands-packages.js b/tools/cli/commands-packages.js
index 86560afc81..7935738229 100644
--- a/tools/cli/commands-packages.js
+++ b/tools/cli/commands-packages.js
@@ -1270,6 +1270,31 @@ var maybeUpdateRelease = function (options) {
releaseTrack + ".");
return 1;
}
+
+ if (release.current && ! release.current.isRecommended() &&
+ options.appDir && ! options.patch) {
+ var releaseVersion = release.current.getReleaseVersion();
+ var newerRecommendedReleases = getLaterReleaseVersions(
+ releaseTrack, releaseVersion);
+ if (!newerRecommendedReleases.length) {
+ // When running 'meteor update' without --release in an app,
+ // using a release that is not recommended and is newer than
+ // any recommended release, don't springboard backwards to
+ // an older, recommended release. Don't update Meteor, or
+ // the app's release. This makes it possible to type `meteor update`
+ // with no arguments from a new RC of Meteor, without performing
+ // the update (and subsequent constraint-solving, etc.) using
+ // the old tool.
+ //
+ // We'll still springboard forwards out of an RC, just not backwards.
+ Console.info("Not updating the release, because this app is at a " +
+ "newer release (" + release.current.name + ") than " +
+ "the latest recommended release " +
+ "(" + latestRelease + ").");
+ return 0;
+ }
+ }
+
if (! release.current || release.current.name !== latestRelease) {
// The user asked for the latest release (well, they "asked for it" by not
// passing --release). We're not currently running the latest release on
@@ -1405,13 +1430,10 @@ var maybeUpdateRelease = function (options) {
// We are not doing a patch update, or a specific release update, so we need
// to try all recommended releases on our track, whose order key is greater
// than the app's.
- var appReleaseInfo = catalog.official.getReleaseVersion(
+ releaseVersionsToTry = getLaterReleaseVersions(
projectContext.releaseFile.releaseTrack,
projectContext.releaseFile.releaseVersion);
- var appOrderKey = (appReleaseInfo && appReleaseInfo.orderKey) || null;
- releaseVersionsToTry = catalog.official.getSortedRecommendedReleaseVersions(
- projectContext.releaseFile.releaseTrack, appOrderKey);
if (! releaseVersionsToTry.length) {
// We could not find any releases newer than the one that we are on, on
// that track, so we are done.
@@ -1423,6 +1445,7 @@ var maybeUpdateRelease = function (options) {
}
}
+ var didPrintMessages = false;
var solutionReleaseVersion = _.find(releaseVersionsToTry, function (versionToTry) {
var releaseRecord = catalog.official.getReleaseVersion(
releaseTrack, versionToTry);
@@ -1435,11 +1458,16 @@ var maybeUpdateRelease = function (options) {
projectContext.resolveConstraints();
});
if (messages.hasMessages()) {
+ // To avoid overloading the user, we only print messages for the first
+ // release version we try (which should be the latest)
+ if (!didPrintMessages) {
+ Console.info(
+ "Update to release", releaseTrack + "@" + versionToTry,
+ "is impossible:");
+ Console.info(messages.formatMessages());
+ didPrintMessages = true;
+ }
// Nope, this release didn't work.
- Console.debug(
- "Update to release", releaseTrack + "@" + versionToTry,
- "is impossible:");
- Console.debug(messages.formatMessages());
return false;
}
@@ -1505,6 +1533,15 @@ var maybeUpdateRelease = function (options) {
return 0;
};
+function getLaterReleaseVersions(releaseTrack, releaseVersion) {
+ var releaseInfo = catalog.official.getReleaseVersion(
+ releaseTrack, releaseVersion);
+ var orderKey = (releaseInfo && releaseInfo.orderKey) || null;
+
+ return catalog.official.getSortedRecommendedReleaseVersions(
+ releaseTrack, orderKey);
+}
+
main.registerCommand({
name: 'update',
options: {
@@ -1638,13 +1675,23 @@ main.registerCommand({
if (!options.args.length) {
// Generate and print info about what is NOT at the latest version.
- var topLevelPkgSet = {};
+
+ var topLevelPkgSet = {}; // direct dependencies (rather than indirect)
projectContext.projectConstraintsFile.eachConstraint(function (constraint) {
topLevelPkgSet[constraint.package] = true;
});
+
+ var releaseConstrainedPkgSet = {}; // pinned core packages (to skip)
+ _.each(releaseRecordForConstraints.packages, function (v, packageName) {
+ releaseConstrainedPkgSet[packageName] = true;
+ });
+
var nonlatestDirectDeps = [];
var nonlatestIndirectDeps = [];
projectContext.packageMap.eachPackage(function (name, info) {
+ if (_.has(releaseConstrainedPkgSet, name)) {
+ return;
+ }
var selectedVersion = info.version;
var catalog = projectContext.projectCatalog;
var latestVersion = getNewerVersion(name, selectedVersion, catalog);
diff --git a/tools/cli/commands.js b/tools/cli/commands.js
index d565d8c21a..ca59b1d6fe 100644
--- a/tools/cli/commands.js
+++ b/tools/cli/commands.js
@@ -342,7 +342,7 @@ function doRunCommand(options) {
if (!_.isEmpty(runTargets)) {
main.captureAndExit('', 'preparing Cordova project', () => {
- cordovaProject = new CordovaProject(projectContext, {
+ const cordovaProject = new CordovaProject(projectContext, {
settingsFile: options.settings,
mobileServerUrl: utils.formatUrl(parsedMobileServerUrl) });
@@ -597,12 +597,12 @@ main.registerCommand({
// If trying to create in current directory
appName = files.pathBasename(files.cwd());
} else {
- appName = files.pathBasename(options.appDir);
+ appName = files.pathBasename(appPath);
}
var transform = function (x) {
- return x.replace(/~name~/g, files.pathBasename(appPath));
+ return x.replace(/~name~/g, appName);
};
// These file extensions are usually metadata, not app code
@@ -724,7 +724,7 @@ ${nonCodeFileExts.join(', ')}
// in the template's versions file).
var appNameToDisplay = appPathAsEntered === "." ?
- "current directory" : appPathAsEntered;
+ "current directory" : `'${appPathAsEntered}'`;
var message = `Created a new Meteor app in ${appNameToDisplay}`;
@@ -741,9 +741,14 @@ ${nonCodeFileExts.join(', ')}
Console.info("To run your new app:");
if (appPathAsEntered !== ".") {
+ // Wrap the app path in quotes if it contains spaces
+ const appPathWithQuotesIfSpaces = appPathAsEntered.indexOf(' ') === -1 ?
+ appPathAsEntered :
+ `'${appPathAsEntered}'`;
+
// Don't tell people to 'cd .'
Console.info(
- Console.command("cd " + appPathAsEntered),
+ Console.command("cd " + appPathWithQuotesIfSpaces),
Console.options({ indent: 2 }));
}
@@ -798,7 +803,7 @@ main.registerCommand(_.extend({ name: 'bundle', hidden: true
"This command has been deprecated in favor of " +
Console.command("'meteor build'") + ", which allows you to " +
"build for multiple platforms and outputs a directory instead of " +
- "a single tarball. See " + Console.command("'meteor help build'") +
+ "a single tarball. See " + Console.command("'meteor help build'") + " " +
"for more information.");
Console.error();
return buildCommand(_.extend(options, { _serverOnly: true }));
@@ -848,7 +853,11 @@ var buildCommand = function (options) {
if (!options._serverOnly) {
cordovaPlatforms = projectContext.platformList.getCordovaPlatforms();
- if (process.platform !== 'darwin' && _.contains(cordovaPlatforms, 'ios')) {
+ if (process.platform === 'win32' && !_.isEmpty(cordovaPlatforms)) {
+ Console.warn(`Can't build for mobile on Windows. Skipping the following \
+platforms: ${cordovaPlatforms.join(", ")}`);
+ cordovaPlatforms = [];
+ } else if (process.platform !== 'darwin' && _.contains(cordovaPlatforms, 'ios')) {
cordovaPlatforms = _.without(cordovaPlatforms, 'ios');
Console.warn("Currently, it is only possible to build iOS apps \
on an OS X system.");
@@ -950,7 +959,7 @@ on an OS X system.");
settingsFile: options.settings,
mobileServerUrl: utils.formatUrl(parsedMobileServerUrl) });
- const plugins = cordova.pluginsFromStarManifest(
+ const plugins = cordova.pluginVersionsFromStarManifest(
bundleResult.starManifest);
cordovaProject.prepareFromAppBundle(bundlePath, plugins);
@@ -984,8 +993,10 @@ https://github.com/meteor/meteor/wiki/How-to-submit-your-iOS-app-to-App-Store
const apkPath = files.pathJoin(buildPath, 'build/outputs/apk',
options.debug ? 'android-debug.apk' : 'android-release-unsigned.apk')
+ if (files.exists(apkPath)) {
files.copyFile(apkPath, files.pathJoin(platformOutputPath,
options.debug ? 'debug.apk' : 'release-unsigned.apk'));
+ }
files.writeFile(
files.pathJoin(platformOutputPath, 'README'),
@@ -1120,16 +1131,16 @@ main.registerCommand({
if (! mongoPort) {
Console.info("mongo: Meteor isn't running a local MongoDB server.");
Console.info();
- Console.info(
- "This command only works while Meteor is running your application " +
- "locally. Start your application first. (This error will also occur " +
- "if you asked Meteor to use a different MongoDB server with " +
- "$MONGO_URL when you ran your application.)");
+ Console.info(`\
+This command only works while Meteor is running your application locally. \
+Start your application first with 'meteor' and then run this command in a new \
+terminal. This error will also occur if you asked Meteor to use a different \
+MongoDB server with $MONGO_URL when you ran your application.`);
Console.info();
- Console.info(
- "If you're trying to connect to the database of an app you deployed " +
- "with " + Console.command("'meteor deploy'") +
- ", specify your site's name with this command.");
+ Console.info(`\
+If you're trying to connect to the database of an app you deployed with \
+${Console.command("'meteor deploy'")}, specify your site's name as an argument \
+to this command.`);
return 1;
}
mongoUrl = "mongodb://127.0.0.1:" + mongoPort + "/meteor";
@@ -1455,7 +1466,11 @@ main.registerCommand({
'allow-incompatible-update': { type: Boolean },
// Don't print linting messages for tested packages
- 'no-lint': { type: Boolean }
+ 'no-lint': { type: Boolean },
+
+ // allow excluding packages when testing all packages.
+ // should be a comma-separated list of package names.
+ 'exclude': { type: String }
},
catalogRefresh: new catalog.Refresh.Never()
}, function (options) {
@@ -1512,6 +1527,16 @@ main.registerCommand({
var packagesToAdd = getTestPackageNames(projectContext, options.args);
+ // filter out excluded packages
+ var excludedPackages = options.exclude && options.exclude.split(',');
+ if (excludedPackages) {
+ packagesToAdd = _.filter(packagesToAdd, function (p) {
+ return ! _.some(excludedPackages, function (excluded) {
+ return p.replace(/^local-test:/, '') === excluded;
+ });
+ });
+ }
+
// Use the driver package
// Also, add `autoupdate` so that you don't have to manually refresh the tests
packagesToAdd.unshift("autoupdate", options['driver-package']);
@@ -1539,7 +1564,7 @@ main.registerCommand({
if (!_.isEmpty(runTargets)) {
main.captureAndExit('', 'preparing Cordova project', () => {
- cordovaProject = new CordovaProject(projectContext, {
+ const cordovaProject = new CordovaProject(projectContext, {
settingsFile: options.settings,
mobileServerUrl: utils.formatUrl(parsedMobileServerUrl) });
@@ -1895,7 +1920,8 @@ main.registerCommand({
browserstack: { type: Boolean },
history: { type: Number },
list: { type: Boolean },
- file: { type: String }
+ file: { type: String },
+ exclude: { type: String }
},
hidden: true,
catalogRefresh: new catalog.Refresh.Never()
@@ -1945,6 +1971,14 @@ main.registerCommand({
}
}
+ var excludeRegexp = undefined;
+ if (options.exclude) {
+ excludeRegexp = compileRegexp(options.exclude);
+ if (! excludeRegexp) {
+ return 1;
+ }
+ }
+
if (options.list) {
selftest.listTests({
onlyChanged: options.changed,
@@ -1970,6 +2004,7 @@ main.registerCommand({
galaxyOnly: options.galaxy,
testRegexp: testRegexp,
fileRegexp: fileRegexp,
+ excludeRegexp: excludeRegexp,
// other options
historyLines: options.history,
clients: clients
diff --git a/tools/cli/main.js b/tools/cli/main.js
index 4a1e5956c7..84f94bc182 100644
--- a/tools/cli/main.js
+++ b/tools/cli/main.js
@@ -534,8 +534,9 @@ Fiber(function () {
process.exit(1);
}
- // Set up git hooks
- if (files.inCheckout()) {
+ // Set up git hooks, but not on Windows because they don't work there and it;s
+ // not worth setting it up at the moment
+ if (files.inCheckout() && process.platform !== "win32") {
var installGitHooks = require('../tool-env/install-git-hooks.js');
installGitHooks();
}
diff --git a/tools/cordova/README.md b/tools/cordova/README.md
index 81124f768d..acd2750a74 100644
--- a/tools/cordova/README.md
+++ b/tools/cordova/README.md
@@ -38,56 +38,56 @@ setting the `cwd` and `env` and catching `CordovaError`s.
### Stages
-* `project.createIfNeeded()`
+* `CordovaProject#createIfNeeded()`
Invoked automatically from the constructor. Makes sure the project has been
created if the `cordova-build` directory does not currently exist.
-* `project.prepareFromAppBundle(bundlePath, plugins, options = {})`
+* `CordovaProject#prepareFromAppBundle(bundlePath, pluginVersions, options = {})`
Uses a builder (see `CordovaBuilder`) to generate the `www` directory,
resources, and `config.xml` based on the app bundle result and
`mobile-config.js`.
-* `project.prepareForPlatform(platform)`
+* `CordovaProject#prepareForPlatform(platform)`
Similar to `cordova prepare `. Synchronizes the project contents
(`www` directory and resources) with a platform directory and installs or
updates plugins in a platform-specific manner (modifying an Xcode project for
instance).
-* `project.buildForPlatform(platform, options = [], extraPaths)`
+* `CordovaProject#buildForPlatform(platform, options = [], extraPaths)`
Similar to `cordova build `. Uses platform-specific build mechanisms
to compile app. Includes everything done in the prepare stage. It is used for
`meteor build`.
-* `async project.run(platform, isDevice, options = [], extraPaths)`
+* `async CordovaProject#run(platform, isDevice, options = [], extraPaths)`
Similar to `cordova run `, except that it doesn't include prepare, so
-you'll have to make sure `project.prepareForPlatform()` is called before.
+you'll have to make sure `CordovaProject#prepareForPlatform()` is called before.
Uses platform-specific mechanisms to run the built app on a device or
emulator/simulator. It is used for `meteor run/test-packages`.
### Managing platforms
-* `project.ensurePlatformsAreSynchronized(platforms =
-* this.cordovaPlatformsInApp)`
+* `CordovaProject#ensurePlatformsAreSynchronized(platforms =
+ this.cordovaPlatformsInApp)`
Ensures the platforms installed in the Cordova project are synchronized with the
app-level platforms (in `.meteor/platforms`). This gets invoked as part of
`meteor add-platform/remove-platform` (from `commands-cordova.js`) and as part
of the build process (from `CordovaBuilder`).
-Uses methods `project.listInstalledPlatforms()`,
-`project.addPlatform(platform)`, and `project.removePlatform(platform)` to call
-into Cordova.
+Uses methods `CordovaProject#listInstalledPlatforms()`,
+`CordovaProject#addPlatform(platform)`, and
+`CordovaProject#removePlatform(platform)` to call into Cordova.
-`project.checkPlatformRequirements(platform)` uses the Cordova platform-specific
-tools to get a list of installation requirements, whether they are satisfied,
-and if not, why. The latter can be very useful and is pretty detailed (telling
-you for instance that `ANDROID_HOME` has been set to a non-existent directory,
-or that the right platform tools cannot be found).
+`CordovaProject#checkPlatformRequirements(platform)` uses the Cordova
+platform-specific tools to get a list of installation requirements, whether they
+are satisfied, and if not, why. The latter can be very useful and is pretty
+detailed (telling you for instance that `ANDROID_HOME` has been set to a
+non-existent directory, or that the right platform tools cannot be found).
We massage the results a little and print them (only if not all requirements for
a platform are satisfied) in a list with checkmarks and crosses. This may need
@@ -101,18 +101,21 @@ feedback about the state of the installation) and before running (from
### Managing plugins
-* `project.ensurePluginsAreSynchronized(plugins, pluginsConfiguration = {})`
+* `CordovaProject#ensurePluginsAreSynchronized(pluginVersions,
+ pluginsConfiguration = {})`
Ensures the plugins installed in the Cordova project are synchronized with the
app-level Cordova plugins. This gets invoked as part of the build process (from
`CordovaBuilder`) where it is passed the plugins from the star manifest in the
-app bundle (a combination of `.meteor/cordova-plugins` for stand-alone plugin
-installs and the plugins added as dependencies of packages through
-`Cordova.depends`). The `pluginsConfiguration` comes from `App.configurePlugin`
-calls in `meteor-config.js`.
+app bundle (`pluginVersionsFromStarManifest`, a combination of
+`.meteor/cordova-plugins` for stand-alone plugin installs and the plugins added
+as dependencies of packages through `Cordova.depends`).
+The `pluginsConfiguration` comes from `App.configurePlugin` calls in
+`meteor-config.js`.
-Uses methods `project.listInstalledPluginVersions()`, `project.addPlugin(name, version,
-config)`, `project.removePlugins(plugins)` to call into Cordova.
+Uses methods `CordovaProject#listInstalledPluginVersions()`,
+`CordovaProject#addPlugin(name, version, config)`,
+`CordovaProject#removePlugins(plugins)` to call into Cordova.
## Running
@@ -129,7 +132,7 @@ One of the main benefits of this is that we can now use the app bundle generated
in `AppRunner`. Previously, we were actually doing a whole separate
`bundler.bundle()` for Cordova, even though the one in `AppRunner` also
generated the `web.cordova` architecture (which is needed to serve it to clients
-as part of Hot Code Push). We now invoke `cordovaRunner.prepareProject()` with
+as part of Hot Code Push). We now invoke `CordovaRunner#prepareProject()` with
the existing bundle result right before starting the server app.
Moving building/running of Cordova apps to `AppRunner` should also allow Cordova
@@ -144,7 +147,7 @@ Right now, we only differentiate between running on a device or an
emulator/simulator. We may want to allow specifying a specific device ID in the
future (so we can select between different connected devices for instance).
-`cordovaRunner.startRunTargets()` is responsible for starting the app on the
+`CordovaRunner#startRunTargets()` is responsible for starting the app on the
associated run targets (while displaying build messages and a run log entry). It
currently starts the targets asynchronously (in parallel). This means running
the app on multiple targets is a lot faster than before. Because we don't
@@ -160,5 +163,5 @@ that is built-up through different stages (`initalizeDefaults()`,
`processControlFile()`) and can be used to generate and copy the `www`
directory, resources, and `config.xml`.
-`builder.processControlFile()` is responsible for running the `mobile-config.js`
-control file (passing itself as a context to `App`).
+`CordovaBuilder#processControlFile()` is responsible for running the
+`mobile-config.js` control file (passing itself as a context to `App`).
diff --git a/tools/cordova/builder.js b/tools/cordova/builder.js
index 12b7585927..92cdd189e6 100644
--- a/tools/cordova/builder.js
+++ b/tools/cordova/builder.js
@@ -79,17 +79,28 @@ export class CordovaBuilder {
// Set some defaults different from the Cordova defaults
this.additionalConfiguration = {
- 'webviewbounce': false,
- 'DisallowOverscroll': true,
- 'deployment-target': '7.0'
+ global: {
+ 'webviewbounce': false,
+ 'DisallowOverscroll': true,
+ 'deployment-target': '7.0'
+ },
+ platform: {
+ ios: {},
+ android: {}
+ }
};
const packageMap = this.projectContext.packageMap;
if (packageMap && packageMap.getInfo('launch-screen')) {
- this.additionalConfiguration.AutoHideSplashScreen = false;
- this.additionalConfiguration.SplashScreen = 'screen';
- this.additionalConfiguration.SplashScreenDelay = 10000;
+ this.additionalConfiguration.global.AutoHideSplashScreen = false;
+ this.additionalConfiguration.global.SplashScreen = 'screen';
+ this.additionalConfiguration.global.SplashScreenDelay = 10000;
+ }
+
+ if (packageMap && packageMap.getInfo('mobile-status-bar')) {
+ this.additionalConfiguration.global.StatusBarOverlaysWebView = false;
+ this.additionalConfiguration.global.StatusBarStyle = 'default';
}
// Default access rules for plain Meteor-Cordova apps.
@@ -209,8 +220,8 @@ export class CordovaBuilder {
email: this.metadata.email
}).txt(this.metadata.author);
- // Set the additional configuration preferences
- _.each(this.additionalConfiguration, (value, key) => {
+ // Set the additional global configuration preferences
+ _.each(this.additionalConfiguration.global, (value, key) => {
config.element('preference', {
name: key,
value: value.toString()
@@ -229,8 +240,20 @@ export class CordovaBuilder {
config.element('access', opts);
});
- const iosPlatformElement = config.element('platform', { name: 'ios' });
- const androidPlatformElement = config.element('platform', { name: 'android' });
+ const platformElement = {
+ ios: config.element('platform', {name: 'ios'}),
+ android: config.element('platform', {name: 'android'})
+ }
+
+ // Set the additional platform-specific configuration preferences
+ _.each(this.additionalConfiguration.platform, (prefs, platform) => {
+ _.each(prefs, (value, key) => {
+ platformElement[platform].element('preference', {
+ name: key,
+ value: value.toString()
+ });
+ });
+ });
if (shouldCopyResources) {
// Prepare the resources folder
@@ -239,10 +262,10 @@ export class CordovaBuilder {
Console.debug('Copying resources for mobile apps');
- this.configureAndCopyImages(iconsIosSizes, iosPlatformElement, 'icon');
- this.configureAndCopyImages(iconsAndroidSizes, androidPlatformElement, 'icon');
- this.configureAndCopyImages(launchIosSizes, iosPlatformElement, 'splash');
- this.configureAndCopyImages(launchAndroidSizes, androidPlatformElement, 'splash');
+ this.configureAndCopyImages(iconsIosSizes, platformElement.ios, 'icon');
+ this.configureAndCopyImages(iconsAndroidSizes, platformElement.android, 'icon');
+ this.configureAndCopyImages(launchIosSizes, platformElement.ios, 'splash');
+ this.configureAndCopyImages(launchAndroidSizes, platformElement.android, 'splash');
}
Console.debug('Writing new config.xml');
@@ -444,10 +467,20 @@ function createAppConfiguration(builder) {
* @param {String} name A preference name supported by Cordova's
* `config.xml`.
* @param {String} value The value for that preference.
+ * @param {String} [platform] Optional. A platform name (either `ios` or `android`) to add a platform-specific preference.
* @memberOf App
*/
- setPreference: function (key, value) {
- builder.additionalConfiguration[key] = value;
+ setPreference: function (key, value, platform) {
+ if (platform) {
+ if (!_.contains(['ios', 'android'], platform)) {
+ throw new Error(`Unknown platform in App.setPreference: ${platform}. \
+Valid platforms are: ios, android.`);
+ }
+
+ builder.additionalConfiguration.platform[platform][key] = value;
+ } else {
+ builder.additionalConfiguration.global[key] = value;
+ }
},
/**
diff --git a/tools/cordova/index.js b/tools/cordova/index.js
index eae279c921..fa5cc60663 100644
--- a/tools/cordova/index.js
+++ b/tools/cordova/index.js
@@ -1,5 +1,7 @@
import _ from 'underscore';
import assert from 'assert';
+import utils from '../utils/utils.js';
+import buildmessage from '../utils/buildmessage.js';
import { oldToNew as oldToNewPluginIds, newToOld as newToOldPluginIds }
from 'cordova-registry-mapper';
@@ -48,34 +50,70 @@ export function splitPluginsAndPackages(packages) {
return result;
}
+// Returns the cordovaDependencies of the Cordova arch from a star manifest.
+export function pluginVersionsFromStarManifest(star) {
+ var cordovaProgram = _.findWhere(star.programs, { arch: CORDOVA_ARCH });
+ return cordovaProgram ? cordovaProgram.cordovaDependencies : {};
+}
+
export function newPluginId(id) {
return oldToNewPluginIds[id];
}
-export function convertPluginVersionsToNewIDs(pluginVersions) {
+export function convertPluginVersions(pluginVersions) {
assert(pluginVersions);
+ buildmessage.assertInJob();
+
+ let newPluginVersions = {};
+
+ _.each(pluginVersions, (version, id) => {
+ if (utils.isUrlWithSha(version)) {
+ version = convertToGitUrl(version);
+ if (!version) {
+ // convertToGitUrl will add an error to buildmessage messages
+ return;
+ }
+ }
- return _.object(_.map(pluginVersions, (version, id) => {
const newId = newPluginId(id);
if (newId) {
- if (_.has(pluginVersions, newId)) {
- // The plugin has already been added using the new ID, so we do not
- // overwrite the version.
- return false;
- } else {
- return [newId, version];
+ // If the plugin has already been added using the new ID, we do not
+ // overwrite the version.
+ if (!_.has(pluginVersions, newId)) {
+ newPluginVersions[newId] = version;
}
} else {
- return [id, version];
+ newPluginVersions[id] = version;
}
- }));
+ });
+
+ return newPluginVersions;
}
-// Returns the cordovaDependencies of the Cordova arch from a star manifest.
-export function pluginsFromStarManifest(star) {
- var cordovaProgram = _.findWhere(star.programs, { arch: CORDOVA_ARCH });
- return cordovaProgram ? cordovaProgram.cordovaDependencies : {};
+// Convert old-style GitHub tarball URLs to new Git URLs, and check if other
+// Git URLs contain a SHA reference.
+export function convertToGitUrl(url) {
+ buildmessage.assertInJob();
+
+ // Matches GitHub tarball URLs, like:
+ // https://github.com/meteor/com.meteor.cordova-update/tarball/92fe99b7248075318f6446b288995d4381d24cd2
+ const match =
+ url.match(/^https?:\/\/github.com\/(.+?)\/(.+?)\/tarball\/([0-9a-f]{40})/);
+ if (match) {
+ const [, organization, repository, sha] = match;
+ // Convert them to a Git URL
+ return `https://github.com/${organization}/${repository}.git#${sha}`;
+ // We only support Git URLs with a SHA reference to guarantee repeatability
+ // of builds
+ } else if (/\.git#[0-9a-f]{40}/.test(url)) {
+ return url;
+ } else {
+ buildmessage.error(`Meteor no longer supports installing Cordova plugins \
+from arbitrary tarball URLs. You can either add a plugin from a Git URL with \
+a SHA reference, or from a local path. (Attempting to install from ${url}.)`);
+ return null;
+ }
}
function displayNameForHostPlatform(platform = process.platform) {
diff --git a/tools/cordova/project.js b/tools/cordova/project.js
index f8d1a0d01a..afd950d71f 100644
--- a/tools/cordova/project.js
+++ b/tools/cordova/project.js
@@ -3,8 +3,9 @@ import util from 'util';
import path from 'path';
import assert from 'assert';
import chalk from 'chalk';
+import semver from 'semver';
-import isopackets from '../tool-env/isopackets.js'
+import isopackets from '../tool-env/isopackets.js';
import files from '../fs/files.js';
import utils from '../utils/utils.js';
import { Console } from '../console/console.js';
@@ -13,6 +14,7 @@ import main from '../cli/main.js';
import httpHelpers from '../utils/http-helpers.js';
import { execFileSync, execFileAsync } from '../utils/processes.js';
+import './protect-string-proto.js'; // must always come before 'cordova-lib'
import { cordova as cordova_lib, events as cordova_events, CordovaError }
from 'cordova-lib';
import cordova_util from 'cordova-lib/src/cordova/util.js';
@@ -20,7 +22,7 @@ import superspawn from 'cordova-lib/src/cordova/superspawn.js';
import PluginInfoProvider from 'cordova-lib/src/PluginInfoProvider.js';
import { AVAILABLE_PLATFORMS, displayNameForPlatform, displayNamesForPlatforms,
- newPluginId, convertPluginVersionsToNewIDs,
+ newPluginId, convertPluginVersions, convertToGitUrl,
installationInstructionsUrlForPlatform } from './index.js';
import { CordovaBuilder } from './builder.js';
@@ -59,6 +61,39 @@ yet supported.`);
createIfNeeded() {
buildmessage.assertInJob();
+ // Check if we have an existing Cordova project directory with outdated
+ // platforms. In that case, we remove the whole directory to avoid issues.
+ if (files.exists(this.projectRoot)) {
+ const installedPlatforms = this.listInstalledPlatforms();
+
+ // XXX Decide whether to update these if we update cordova-lib.
+ // If we can guarantee there are no issues going forward, we may want to
+ // use updatePlatforms instead of removing the whole directory.
+ const minPlatformVersions = {
+ 'android': '4.1.0',
+ 'ios': '3.9.0'
+ }
+
+ const outdated = _.some(minPlatformVersions, (minVersion, platform) => {
+ // If the platform is not installed, it cannot be outdated
+ if (!_.contains(installedPlatforms, platform)) return false;
+
+ const installedVersion = this.installedVersionForPlatform(platform);
+ // If we cannot establish the installed version, we consider it outdated
+ if (!installedVersion) return true;
+
+ return semver.lt(installedVersion, minVersion);
+ });
+
+ if (outdated) {
+ Console.debug(`Removing Cordova project directory to avoid issues with
+outdated platforms`);
+ // Remove Cordova project directory to start afresh
+ // and avoid a broken project
+ files.rm_recursive(this.projectRoot);
+ }
+ }
+
if (!files.exists(this.projectRoot)) {
// We create a temporary directory with a generated config.xml
// to use as a template for creating the Cordova project
@@ -172,7 +207,7 @@ ${displayNameForPlatform(platform)} with options ${options}`,
cwd: this.projectRoot,
stdio: Console.verbose ? 'inherit' : 'pipe',
waitForClose: false })
- );
+ ), null, null;
}
// Platforms
@@ -222,15 +257,25 @@ ${displayNameForPlatform(platform)}`);
if (!satisfied) {
Console.info();
- Console.info(`Not all installation requirements are satisfied \
-for building and running apps for ${displayNameForPlatform(platform)}.`);
+ Console.info(`Your system does not yet seem to fulfill all requirements \
+to build apps for ${displayNameForPlatform(platform)}.`);
+
const url = installationInstructionsUrlForPlatform(platform);
if (url) {
- Console.info("Please follow the instructions here:");
+ Console.info();
+ Console.info("Please follow the installation instructions here:");
Console.info(Console.url(url));
}
+
Console.info();
- Console.info("More details about the individual requirements:");
+
+ if (!Console.verbose) {
+ Console.info("Specify the --verbose option to see more details about \
+the status of individual requirements.");
+ return false;
+ }
+
+ Console.info("Status of the requirements:");
for (requirement of requirements) {
const name = requirement.name;
if (requirement.installed) {
@@ -252,6 +297,21 @@ for building and running apps for ${displayNameForPlatform(platform)}.`);
return cordova_util.listPlatforms(files.convertToOSPath(this.projectRoot));
}
+ installedVersionForPlatform(platform) {
+ const command = files.convertToOSPath(files.pathJoin(
+ this.projectRoot, 'platforms', platform, 'cordova', 'version'));
+ // Make sure the command exists before trying to execute it
+ if (files.exists(command)) {
+ return this.runCommands(
+ `getting installed version for platform ${platform} in Cordova project`,
+ execFileSync(command, {
+ env: this.defaultEnvWithPathsAdded(),
+ cwd: this.projectRoot}), null, null);
+ } else {
+ return null;
+ }
+ }
+
updatePlatforms(platforms = this.listInstalledPlatforms()) {
this.runCommands(`updating Cordova project for platforms \
${displayNamesForPlatforms(platforms)}`, async () => {
@@ -352,8 +412,10 @@ from Cordova project`, async () => {
assert(id);
assert(version);
+ buildmessage.assertInJob();
+
if (utils.isUrlWithSha(version)) {
- return this.convertToGitUrl(version);
+ return convertToGitUrl(version);
} else if (utils.isUrlWithFileScheme(version)) {
// Strip file:// and resolve the path relative to the cordova-build
// directory
@@ -373,29 +435,6 @@ from Cordova project`, async () => {
}
}
- // Convert old-style GitHub tarball URLs to new Git URLs, and check if other
- // Git URLs contain a SHA reference.
- convertToGitUrl(url) {
- // Matches GitHub tarball URLs, like:
- // https://github.com/meteor/com.meteor.cordova-update/tarball/92fe99b7248075318f6446b288995d4381d24cd2
- const match =
- url.match(/^https?:\/\/github.com\/(.+?)\/(.+?)\/tarball\/([0-9a-f]{40})/);
- if (match) {
- const [, organization, repository, sha] = match;
- // Convert them to a Git URL
- return `https://github.com/${organization}/${repository}.git#${sha}`;
- // We only support Git URLs with a SHA reference to guarantee repeatability
- // of builds
- } else if (/\.git#[0-9a-f]{40}/.test(url)) {
- return url;
- } else {
- buildmessage.error(`Meteor no longer supports installing Cordova plugins \
-from arbitrary tarball URLs. You can either add a plugin from a Git URL with \
-a SHA reference, or from a local path. (Attempting to install from ${url}.)`);
- return null;
- }
- }
-
// Strips file:// and resolves the path relative to the cordova-build
// directory
resolveLocalPluginPath(pluginPath) {
@@ -437,32 +476,37 @@ from Cordova project`, async () => {
buildmessage.assertInCapture();
- // Cordova plugin IDs have changed as part of moving to npm.
- // We convert old plugin IDs to new IDs in the 1.2.0-cordova-changes
- // upgrader and when adding plugins, but packages may still depend on
- // the old IDs.
- // To avoid attempts at duplicate installation, we check for old IDs here
- // and convert them to new IDs when needed.
- pluginVersions = convertPluginVersionsToNewIDs(pluginVersions);
-
- // Also, we warn if any App.configurePlugin calls in mobile-config.js
- // need to be updated (and in the meantime we take care of the
- // conversion of the plugin configuration to the new ID).
- pluginsConfiguration = _.object(_.map(pluginsConfiguration, (config, id) => {
- const newId = newPluginId(id);
- if (newId) {
- Console.warn();
- Console.labelWarn(`Cordova plugin ${id} has been renamed to ${newId} \
-as part of moving to npm. Please change the App.configurePlugin call in \
-mobile-config.js accordingly.`);
- return [newId, config];
- } else {
- return [id, config];
- }
- }));
-
buildmessage.enterJob({ title: "installing Cordova plugins"}, () => {
- const installedPluginVersions = this.listInstalledPluginVersions();
+ // Cordova plugin IDs have changed as part of moving to npm.
+ // We convert old plugin IDs to new IDs in the 1.2.0-cordova-changes
+ // upgrader and when adding plugins, but packages may still depend on
+ // the old IDs.
+ // To avoid attempts at duplicate installation, we check for old IDs here
+ // and convert them to new IDs when needed. We also convert old-style GitHub
+ // tarball URLs to new Git URLs, and check if other Git URLs contain a
+ // SHA reference.
+ pluginVersions = convertPluginVersions(pluginVersions);
+
+ if (buildmessage.jobHasMessages()) return;
+
+ // Also, we warn if any App.configurePlugin calls in mobile-config.js
+ // need to be updated (and in the meantime we take care of the
+ // conversion of the plugin configuration to the new ID).
+ pluginsConfiguration = _.object(_.map(pluginsConfiguration, (config, id) => {
+ const newId = newPluginId(id);
+ if (newId) {
+ Console.warn();
+ Console.labelWarn(`Cordova plugin ${id} has been renamed to ${newId} \
+ as part of moving to npm. Please change the App.configurePlugin call in \
+ mobile-config.js accordingly.`);
+ return [newId, config];
+ } else {
+ return [id, config];
+ }
+ }));
+
+ const installedPluginVersions =
+ convertPluginVersions(this.listInstalledPluginVersions());
// Due to the dependency structure of Cordova plugins, it is impossible to
// upgrade the version on an individual Cordova plugin. Instead, whenever
@@ -481,10 +525,6 @@ mobile-config.js accordingly.`);
if (isPluginFromLocalPath) {
pluginsFromLocalPath[id] = version;
} else {
- if (utils.isUrlWithSha(version)) {
- version = this.convertToGitUrl(version);
- }
-
if (!_.has(installedPluginVersions, id) ||
installedPluginVersions[id] !== version) {
// We do not have the plugin installed or the version has changed.
diff --git a/tools/cordova/protect-string-proto.js b/tools/cordova/protect-string-proto.js
new file mode 100644
index 0000000000..020a9db457
--- /dev/null
+++ b/tools/cordova/protect-string-proto.js
@@ -0,0 +1,18 @@
+// `cordova-lib` depends on `shelljs`, which modifies String.prototype
+// (which is BAD). See:
+// https://github.com/arturadib/shelljs/issues/159
+//
+// The following code protects the tool environment (which is also
+// where build plugins run) from having a polluted String.prototype.
+// One JS library in particular, String.js (before v3.3.1), is
+// sensitive to String prototype pollution.
+//
+// Fortunately, `cordova-lib` does not seem to rely on the presence of
+// `String#to` or `String#toEnd` (or this code would break it).
+//
+// This code can be removed when `shelljs` cleans up its act and
+// `cordova-lib` uses a new version, or when `cordova-lib` moves away
+// from `shelljs`.
+
+Object.defineProperty(String.prototype, 'to', { set: function () {} });
+Object.defineProperty(String.prototype, 'toEnd', { set: function () {} });
diff --git a/tools/cordova/run-targets.js b/tools/cordova/run-targets.js
index c5e55e1f38..bd31be3d42 100644
--- a/tools/cordova/run-targets.js
+++ b/tools/cordova/run-targets.js
@@ -30,19 +30,21 @@ export class iOSRunTarget extends CordovaRunTarget {
// ios-deploy is super buggy, so we just open Xcode and let the user
// start the app themselves.
if (this.isDevice) {
- // Make sure we prepare the platform, which is normally done as part of
- // running
- cordovaProject.prepareForPlatform(this.platform);
-
openXcodeProject(files.pathJoin(cordovaProject.projectRoot,
'platforms', 'ios'));
} else {
await cordovaProject.run(this.platform, this.isDevice, undefined);
- // Bring iOS Simulator to front
- child_process.spawn('osascript', ['-e',
- 'tell application "System Events" \
- to set frontmost of process "iOS Simulator" to true']);
+ // Bring iOS Simulator to front (it is called Simulator in Xcode 7)
+ execFileAsync('osascript', ['-e',
+`tell application "System Events"
+ set possibleSimulatorNames to {"iOS Simulator", "Simulator"}
+ repeat with possibleSimulatorName in possibleSimulatorNames
+ if application process possibleSimulatorName exists then
+ set frontmost of process possibleSimulatorName to true
+ end if
+ end repeat
+end tell`]);
}
}
}
diff --git a/tools/cordova/runner.js b/tools/cordova/runner.js
index 599027f4f6..cc5c08cac2 100644
--- a/tools/cordova/runner.js
+++ b/tools/cordova/runner.js
@@ -71,34 +71,35 @@ export class CordovaRunner {
}
}
- prepareProject(bundlePath, plugins, options) {
+ prepareProject(bundlePath, pluginVersions, options) {
buildmessage.assertInCapture();
buildmessage.enterJob({ title: "preparing Cordova project" }, () => {
- this.cordovaProject.prepareFromAppBundle(bundlePath, plugins, options);
+ this.cordovaProject.prepareFromAppBundle(bundlePath,
+ pluginVersions, options);
+
+ if (buildmessage.jobHasMessages()) return;
for (platform of this.platformsForRunTargets) {
this.cordovaProject.prepareForPlatform(platform);
}
});
- this.platforms = this.platformsForRunTargets;
- this.plugins = plugins;
+ this.pluginVersions = pluginVersions;
}
startRunTargets() {
- buildmessage.assertInCapture();
-
this.started = false;
for (runTarget of this.runTargets) {
- buildmessage.enterJob({ title: `starting ${runTarget.title}` }, () => {
+ const messages = buildmessage.capture({ title: `starting ${runTarget.title}` }, () => {
Promise.await(runTarget.start(this.cordovaProject));
-
- if (!buildmessage.jobHasMessages()) {
- runLog.log(`Started ${runTarget.title}.`, { arrow: true });
- }
});
+ if (messages.hasMessages()) {
+ Console.printMessages(messages);
+ } else {
+ runLog.log(`Started ${runTarget.title}.`, { arrow: true });
+ }
}
this.started = true;
@@ -114,7 +115,8 @@ export class CordovaRunner {
_.intersection(platformsForRunTargets, cordovaPlatformsInApp));
}
- havePluginsChangedSinceLastRun(plugins) {
- return this.plugins && !_.isEqual(this.plugins, plugins);
+ havePluginsChangedSinceLastRun(pluginVersions) {
+ return this.pluginVersions &&
+ !_.isEqual(this.pluginVersions, pluginVersions);
}
}
diff --git a/tools/isobuild/build-plugin.js b/tools/isobuild/build-plugin.js
index 4dda5d0072..7c47789665 100644
--- a/tools/isobuild/build-plugin.js
+++ b/tools/isobuild/build-plugin.js
@@ -322,7 +322,7 @@ class SourceClassification {
// `api.addFiles('foo.bar')` where *.bar is a web-specific legacy
// handler (eg) would end up adding 'foo.bar' as a static asset on
// non-web programs, which was unintended. This didn't happen in apps
- // because initFromAppDir's getSourcesFunc never added them.)
+ // because initFromAppDir's getFiles never added them.)
const filteredSourceProcessors = sourceProcessors.filter(
(sourceProcessor) => sourceProcessor.relevantForArch(arch)
);
@@ -470,9 +470,14 @@ _.extend(exports.InputFile.prototype, {
*/
error: function (options) {
var self = this;
- var relPath = self.getPathInPackage();
- buildmessage.error(options.message || ("error building " + relPath), {
- file: options.sourcePath || relPath,
+ var path = self.getPathInPackage();
+ var packageName = self.getPackageName();
+ if (packageName) {
+ path = "packages/" + packageName + "/" + path;
+ }
+
+ buildmessage.error(options.message || ("error building " + path), {
+ file: options.sourcePath || path,
line: options.line ? options.line : undefined,
column: options.column ? options.column : undefined,
func: options.func ? options.func : undefined
diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js
index 4ff96adfa2..0793438699 100644
--- a/tools/isobuild/compiler-plugin.js
+++ b/tools/isobuild/compiler-plugin.js
@@ -365,6 +365,14 @@ _.extend(ResourceSlot.prototype, {
if (! self.sourceProcessor && self.inputResource.extension !== "js")
throw Error("addJavaScript on non-source ResourceSlot?");
+ // By default, use the 'bare' option given to addFiles, but allow the option
+ // passed to addJavaScript to override it.
+ var bare = self.inputResource.fileOptions &&
+ self.inputResource.fileOptions.bare;
+ if (options.hasOwnProperty('bare')) {
+ bare = options.bare;
+ }
+
var data = new Buffer(
files.convertToStandardLineEndings(options.data), 'utf8');
self.jsOutputResources.push({
@@ -377,7 +385,7 @@ _.extend(ResourceSlot.prototype, {
// XXX do we need to call convertSourceMapPaths here like we did
// in legacy handlers?
sourceMap: options.sourceMap,
- bare: options.bare
+ bare: !! bare
});
},
addAsset: function (options) {
diff --git a/tools/isobuild/compiler.js b/tools/isobuild/compiler.js
index 17191c237c..168bc822d0 100644
--- a/tools/isobuild/compiler.js
+++ b/tools/isobuild/compiler.js
@@ -301,13 +301,12 @@ var lintUnibuild = function ({isopack, isopackCache, sourceArch}) {
throw Error(`No ${ sourceArch.arch } unibuild for ${ isopack.name }!`);
}
- var sourceItems = sourceArch.getSourcesFunc(
- sourceProcessorSet, unibuild.watchSet);
+ const {sources} = sourceArch.getFiles(sourceProcessorSet, unibuild.watchSet);
const linterMessages = buildmessage.capture(() => {
runLinters({
isopackCache,
- sourceItems,
+ sources,
sourceProcessorSet,
inputSourceArch: sourceArch,
watchSet: unibuild.watchSet
@@ -324,20 +323,20 @@ var lintUnibuild = function ({isopack, isopackCache, sourceArch}) {
var compileUnibuild = function (options) {
buildmessage.assertInCapture();
- var isopk = options.isopack;
- var inputSourceArch = options.sourceArch;
- var isopackCache = options.isopackCache;
- var nodeModulesPath = options.nodeModulesPath;
- var isPortable = options.isPortable;
- var noLineNumbers = options.noLineNumbers;
+ const isopk = options.isopack;
+ const inputSourceArch = options.sourceArch;
+ const isopackCache = options.isopackCache;
+ const nodeModulesPath = options.nodeModulesPath;
+ const isPortable = options.isPortable;
+ const noLineNumbers = options.noLineNumbers;
- var isApp = ! inputSourceArch.pkg.name;
- var resources = [];
- var pluginProviderPackageNames = {};
- var watchSet = inputSourceArch.watchSet.clone();
+ const isApp = ! inputSourceArch.pkg.name;
+ const resources = [];
+ const pluginProviderPackageNames = {};
+ const watchSet = inputSourceArch.watchSet.clone();
// *** Determine and load active plugins
- var activePluginPackages = getActivePluginPackages(isopk, {
+ const activePluginPackages = getActivePluginPackages(isopk, {
uses: inputSourceArch.uses,
isopackCache: isopackCache,
// If other package is built from source, then we need to rebuild this
@@ -375,21 +374,23 @@ var compileUnibuild = function (options) {
isopack: isopk
});
if (buildmessage.jobHasMessages()) {
- // Recover by not calling getSourcesFunc and pretending there are no
+ // Recover by not calling getFiles and pretending there are no
// items.
sourceProcessorSet = null;
}
});
// *** Determine source files
- // Note: the getSourcesFunc function isn't expected to add its
+ // Note: the getFiles function isn't expected to add its
// source files to watchSet; rather, the watchSet is for other
- // things that the getSourcesFunc consulted (such as directory
+ // things that the getFiles consulted (such as directory
// listings or, in some hypothetical universe, control files) to
// determine its source files.
- const sourceItems = sourceProcessorSet
- ? inputSourceArch.getSourcesFunc(sourceProcessorSet, watchSet)
- : [];
+ const {
+ sources = [],
+ assets = []
+ } = sourceProcessorSet ?
+ inputSourceArch.getFiles(sourceProcessorSet, watchSet) : {};
if (nodeModulesPath) {
// If this slice has node modules, we should consider the shrinkwrap file
@@ -403,16 +404,19 @@ var compileUnibuild = function (options) {
// an indirect dependency of the coffee-script npm module used by the
// coffeescript package will correctly cause packages with *.coffee files
// to be rebuilt.
- var shrinkwrapPath = nodeModulesPath.replace(
+ const shrinkwrapPath = nodeModulesPath.replace(
/node_modules$/, 'npm-shrinkwrap.json');
watch.readAndWatchFile(watchSet, shrinkwrapPath);
}
- // *** Process each source file
- var addAsset = function (contents, relPath, hash) {
- // XXX hack
- if (! inputSourceArch.pkg.name)
+ // This function needs to be factored out to support legacy handlers later on
+ // in the compilation process
+ function addAsset(contents, relPath, hash) {
+ // XXX hack to strip out private and public directory names from app asset
+ // paths
+ if (! inputSourceArch.pkg.name) {
relPath = relPath.replace(/^(private|public)\//, '');
+ }
resources.push({
type: "asset",
@@ -422,85 +426,94 @@ var compileUnibuild = function (options) {
files.pathJoin(inputSourceArch.pkg.serveRoot, relPath)),
hash: hash
});
- };
+ }
- _.each(sourceItems, function (source) {
- var relPath = source.relPath;
- var fileOptions = _.clone(source.fileOptions) || {};
- var absPath = files.pathResolve(inputSourceArch.pkg.sourceRoot, relPath);
- var filename = files.pathBasename(relPath);
+ // Add all assets
+ _.values(assets).forEach((asset) => {
+ const relPath = asset.relPath;
+ const absPath = files.pathResolve(inputSourceArch.pkg.sourceRoot, relPath);
- // Find the handler for source files with this extension (unless explicitly
- // tagged as a static asset).
+ // readAndWatchFileWithHash returns an object carrying a buffer with the
+ // file-contents. The buffer contains the original data of the file (no EOL
+ // transforms from the tools/files.js part).
+ const file = watch.readAndWatchFileWithHash(watchSet, absPath);
+ const hash = file.hash;
+ const contents = file.contents;
+
+ addAsset(contents, relPath, hash);
+ });
+
+ // Add and compile all source files
+ _.values(sources).forEach((source) => {
+ const relPath = source.relPath;
+ const fileOptions = _.clone(source.fileOptions) || {};
+ const absPath = files.pathResolve(inputSourceArch.pkg.sourceRoot, relPath);
+ const filename = files.pathBasename(relPath);
+
+ // Find the handler for source files with this extension
let classification = null;
- if (! fileOptions.isAsset) {
- classification = sourceProcessorSet.classifyFilename(
+ classification = sourceProcessorSet.classifyFilename(
+ filename, inputSourceArch.arch);
+
+ if (classification.type === 'wrong-arch') {
+ // This file is for a compiler plugin but not for this arch. Skip it,
+ // and don't even watch it. (eg, skip CSS preprocessor files on the
+ // server.) This `return` skips this source file and goes on to the next
+ // one.
+ return;
+ }
+
+ if (classification.type === 'unmatched') {
+ // This is not matched by any compiler plugin or legacy source handler,
+ // but it was added as a source file.
+ //
+ // Prior to the batch-plugins project, these would be implicitly treated
+ // as static assets. Now we consider this to be an error; you need to
+ // explicitly tell that you want something to be a static asset by calling
+ // addAssets or putting it in the public/private directories in an app.
+ //
+ // This is a backwards-incompatible change, but it doesn't affect
+ // previously-published packages (because the check is occuring in the
+ // compiler), and it doesn't affect apps (where random files outside of
+ // private/public never end up in the source list anyway).
+ //
+ // As one special case, if a file is unmatched by the compiler
+ // SourceProcessorSet but is matched by the linter SourceProcessorSet (ie,
+ // a linter config file), we don't report an error; this is so that you
+ // can run `api.addFiles('.jshintrc')` and have it work. (This is only
+ // relevant for packages.) We don't put these files in the WatchSet,
+ // though; that happens via compiler.lint.
+
+ if (isApp) {
+ // This shouldn't happen, because initFromAppDir's getFiles
+ // should only return assets or sources which match
+ // sourceProcessorSet.
+ throw Error("app contains non-asset files without plugin? " +
+ relPath + " - " + filename);
+ }
+
+ const linterClassification = linterSourceProcessorSet.classifyFilename(
filename, inputSourceArch.arch);
-
- if (classification.type === 'wrong-arch') {
- // This file is for a compiler plugin but not for this arch. Skip it,
- // and don't even watch it. (eg, skip CSS preprocessor files on the
- // server.) This `return` goes on to the next `_.each(sourceItems`
- // iteration.
+ if (linterClassification.type !== 'unmatched') {
+ // The linter knows about this, so we'll just ignore it instead of
+ // throwing an error.
return;
}
- if (classification.type === 'unmatched') {
- // This is not matched by any compiler plugin or legacy source handler,
- // and it wasn't explicitly tagged as a static asset (with {isAsset:
- // true} in packages, or by being in private/public in apps).
- //
- // Prior to the batch-plugins project, these would be implicitly treated
- // as static assets. Now we consider this to be an error; you need to
- // explicitly tell that you want something to be a static asset by
- // including the isAsset flag in packages, or by putting it in
- // public/private in an app.
- //
- // This is a backwards-incompatible change, but it doesn't affect
- // previously-published packages (because the check is occuring in the
- // compiler), and it doesn't affect apps (where random files outside of
- // private/public never end up in the source list anyway).
- //
- // As one special case, if a file is unmatched by the compiler
- // SourceProcessorSet but is matched by the linker SourceProcessorSet
- // (ie, a linter config file), we don't report an error; this is so that
- // you can run `api.addFiles('.jshintrc')` and have it work and not also
- // add the file as a static asset. (This is only relevant for
- // packages.) We don't put these files in the WatchSet, though; that
- // happens via compiler.lint.
-
- if (isApp) {
- // This shouldn't happen, because initFromAppDir's getSourcesFunc
- // should only return sources that have isAsset set or which match
- // sourceProcessorSet.
- throw Error("app contains non-asset files without plugin? " +
- relPath + " - " + filename);
- }
-
- const linterClassification = linterSourceProcessorSet.classifyFilename(
- filename, inputSourceArch.arch);
- if (linterClassification.type !== 'unmatched') {
- // The linter knows about this, so we'll just ignore it instead of
- // throwing an error.
- return;
- }
-
- buildmessage.error(
- `No plugin known to handle file '${ relPath }'. If you want this ` +
- `file to be a static asset, pass the {isAsset: true} option ` +
- `to api.addFiles; eg, api.addFiles('${relPath}', 'client', ` +
- `{isAsset: true}).`);
- // recover by ignoring
- return;
- }
+ buildmessage.error(
+ `No plugin known to handle file '${ relPath }'. If you want this \
+file to be a static asset, use addAssets instead of addFiles; eg, \
+api.addAssets('${relPath}', 'client').`);
+ // recover by ignoring
+ return;
}
// readAndWatchFileWithHash returns an object carrying a buffer with the
// file-contents. The buffer contains the original data of the file (no EOL
// transforms from the tools/files.js part).
- var file = watch.readAndWatchFileWithHash(watchSet, absPath);
- var hash = file.hash;
- var contents = file.contents;
+ const file = watch.readAndWatchFileWithHash(watchSet, absPath);
+ const hash = file.hash;
+ const contents = file.contents;
Console.nudge(true);
@@ -523,12 +536,6 @@ var compileUnibuild = function (options) {
return;
}
- // Add static assets.
- if (fileOptions.isAsset) {
- addAsset(contents, relPath, hash);
- return;
- }
-
if (classification.isNonLegacySource()) {
// This is source used by a new-style compiler plugin; it will be fully
// processed later in the bundler.
@@ -578,9 +585,11 @@ var compileUnibuild = function (options) {
// Contains non-portable compiled npm modules, so set arch correctly
arch = archinfo.host();
}
+
+ let nodeModulesPathOrUndefined = nodeModulesPath;
if (! archinfo.matches(arch, "os")) {
// npm modules only work on server architectures
- nodeModulesPath = undefined;
+ nodeModulesPathOrUndefined = undefined;
}
// *** Output unibuild object
@@ -590,7 +599,7 @@ var compileUnibuild = function (options) {
uses: inputSourceArch.uses,
implies: inputSourceArch.implies,
watchSet: watchSet,
- nodeModulesPath: nodeModulesPath,
+ nodeModulesPath: nodeModulesPathOrUndefined,
declaredExports: declaredExports,
resources: resources
});
@@ -600,7 +609,7 @@ var compileUnibuild = function (options) {
};
};
-function runLinters({inputSourceArch, isopackCache, sourceItems,
+function runLinters({inputSourceArch, isopackCache, sources,
sourceProcessorSet, watchSet}) {
// The buildmessage context here is for linter warnings only! runLinters
// should not do anything that can have a real build failure.
@@ -664,10 +673,11 @@ function runLinters({inputSourceArch, isopackCache, sourceItems,
// sourceProcessor.id -> {sourceProcessor, sources: [WrappedSourceItem]}
const sourceItemsForLinter = {};
- sourceItems.forEach((sourceItem) => {
+ _.values(sources).forEach((sourceItem) => {
const { relPath } = sourceItem;
const classification = sourceProcessorSet.classifyFilename(
files.pathBasename(relPath), inputSourceArch.arch);
+
// If we don't have a linter for this file (or we do but it's only on
// another arch), skip without even reading the file into a WatchSet.
if (classification.type === 'wrong-arch' ||
@@ -863,6 +873,9 @@ export function isIsobuildFeaturePackage(packageName) {
return packageName.startsWith('isobuild:');
}
+// If you update this data structure to add more feature packages, you should
+// update the wiki page here:
+// https://github.com/meteor/meteor/wiki/Isobuild-Feature-Packages
export const KNOWN_ISOBUILD_FEATURE_PACKAGES = {
// This package directly calls Plugin.registerCompiler. Package authors
// must explicitly depend on this feature package to use the API.
@@ -902,4 +915,13 @@ export const KNOWN_ISOBUILD_FEATURE_PACKAGES = {
// This package uses the `prodOnly` metadata flag, which causes it to
// automatically depend on the `isobuild:prod-only` feature package.
'isobuild:prod-only': ['1.0.0'],
+
+ // This package depends on a specific version of Cordova. Package authors must
+ // explicitly depend on this feature package to indicate that they are not
+ // compatible with earlier Cordova versions, which is most likely a result of
+ // the Cordova plugins they depend on.
+ // A common scenario is a package depending on a Cordova plugin or version
+ // that is only available on npm, which means downloading the plugin is not
+ // supported on versions of Cordova below 5.0.0.
+ 'isobuild:cordova': ['5.2.0']
};
diff --git a/tools/isobuild/isopack.js b/tools/isobuild/isopack.js
index dcc3db730f..3fcf0d214e 100644
--- a/tools/isobuild/isopack.js
+++ b/tools/isobuild/isopack.js
@@ -1605,6 +1605,7 @@ _.extend(Isopack.prototype, {
/^tools\/meteor-services\/[^\/]+\.js$/,
/^tools\/tool-testing\/[^\/]+\.js$/,
/^tools\/console\/[^\/]+\.js$/,
+ /^tools\/cordova\/[^\/]+\.js$/,
// We don't support running self-test from an install anymore
];
diff --git a/tools/isobuild/meteor-npm.js b/tools/isobuild/meteor-npm.js
index c0de4d7447..db744e81f2 100644
--- a/tools/isobuild/meteor-npm.js
+++ b/tools/isobuild/meteor-npm.js
@@ -188,6 +188,15 @@ var updateExistingNpmDirectory = function (packageName, newPackageNpmDir,
files.rm_recursive(nodeModulesDir);
}
+ // If the node modules directory exists but doesn't have .package.json and
+ // .npm-shrinkwrap.json, recreate. This is to ensure that
+ // providePackageJSONForUnavailableBinaryDeps works.
+ if (files.exists(nodeModulesDir) &&
+ (!files.exists(files.pathJoin(nodeModulesDir, '.package.json')) ||
+ !files.exists(files.pathJoin(nodeModulesDir, '.npm-shrinkwrap.json')))) {
+ files.rm_recursive(nodeModulesDir);
+ }
+
// Make sure node_modules is present (fix for #1761). Prevents npm install
// from installing to an existing node_modules dir higher up in the
// filesystem. node_modules may be absent due to a change in Node version or
@@ -295,8 +304,15 @@ var completeNpmDirectory = function (packageName, newPackageNpmDir,
// Create a shrinkwrap file.
shrinkwrap(newPackageNpmDir);
- // now delete package.json
- files.unlink(files.pathJoin(newPackageNpmDir, 'package.json'));
+ // now get package.json out of the way, but put it somewhere where the
+ // providePackageJSONForUnavailableBinaryDeps code can find it
+ files.rename(
+ files.pathJoin(newPackageNpmDir, 'package.json'),
+ files.pathJoin(newPackageNpmDir, 'node_modules', '.package.json'));
+ // And stow a copy of npm-shrinkwrap too.
+ files.copyFile(
+ files.pathJoin(newPackageNpmDir, 'npm-shrinkwrap.json'),
+ files.pathJoin(newPackageNpmDir, 'node_modules', '.npm-shrinkwrap.json'));
createReadme(newPackageNpmDir);
createNodeVersion(newPackageNpmDir);
diff --git a/tools/isobuild/package-api.js b/tools/isobuild/package-api.js
index 0094bd48a5..b1714e6e21 100644
--- a/tools/isobuild/package-api.js
+++ b/tools/isobuild/package-api.js
@@ -77,8 +77,10 @@ function PackageAPI (options) {
self.buildingIsopackets = !!options.buildingIsopackets;
- // source files used. Map arch -> relPath -> {relPath, fileOptions}
- self.sources = {};
+ // source files used.
+ // It's a multi-level map structured as:
+ // arch -> sources|assets -> relPath -> {relPath, fileOptions}
+ self.files = {};
// symbols exported
self.exports = {};
@@ -90,7 +92,11 @@ function PackageAPI (options) {
self.implies = {};
_.each(compiler.ALL_ARCHES, function (arch) {
- self.sources[arch] = {};
+ self.files[arch] = {
+ assets: [],
+ sources: []
+ };
+
self.exports[arch] = [];
self.uses[arch] = [];
self.implies[arch] = [];
@@ -297,23 +303,70 @@ _.extend(PackageAPI.prototype, {
/**
* @memberOf PackageAPI
* @instance
- * @summary Specify the source code for your package.
+ * @summary Specify source code files for your package.
* @locus package.js
- * @param {String|String[]} filename Name of the source file, or array of
- * strings of source file names.
- * @param {String|String[]} [architecture] If you only want to export the file
- * on the server (or the client), you can pass in the second argument
+ * @param {String|String[]} filenames Paths to the source files.
+ * @param {String|String[]} [architecture] If you only want to use the file
+ * on the server (or the client), you can pass this argument
* (e.g., 'server', 'client', 'web.browser', 'web.cordova') to specify
* what architecture the file is used with. You can specify multiple
- * architectures by passing in an array, for example `['web.cordova', 'os.linux']`.
- * @param {Object} [fileOptions] Options that will be passed to build
- * plugins. For example, for JavaScript files, you can pass `{bare: true}`
- * to not wrap the individual file in its own closure. To add a static asset,
- * pass `{isAsset: true}`; use the `architecture` parameter to determine
- * if this is a client-side asset served by the HTTP server or a server-side
- * asset accessible to the `Assets` APIs.
+ * architectures by passing in an array, for example
+ * `['web.cordova', 'os.linux']`. By default, the file will be loaded on both
+ * server and client.
+ * @param {Object} [options] Options that will be passed to build
+ * plugins.
+ * @param {Boolean} [options.bare] If this file is JavaScript code or will
+ * be compiled into JavaScript code by a build plugin, don't wrap the
+ * resulting file in a closure. Has the same effect as putting a file into the
+ * `client/compatibility` directory in an app.
*/
addFiles: function (paths, arch, fileOptions) {
+ if (fileOptions && fileOptions.isAsset) {
+ buildmessage.error('The `isAsset` option to `addFiles` is deprecated. ' +
+ 'Use PackageAPI#addAssets instead.', { useMyCaller: true });
+ return;
+ }
+
+ // Watch out - we rely on the levels of stack traces inside this
+ // function so don't wrap it in another function without changing that logic
+ this._addFiles("sources", paths, arch, fileOptions);
+ },
+
+ /**
+ * @memberOf PackageAPI
+ * @instance
+ * @summary Specify asset files for your package. They can be accessed via
+ * the [Assets API](#assets) from the server, or at the URL
+ * `/packages/username_package-name/file-name` from the client, depending on the
+ * architecture passed.
+ * @locus package.js
+ * @param {String|String[]} filenames Paths to the asset files.
+ * @param {String|String[]} architecture Specify where this asset should be
+ * available (e.g., 'server', 'client', 'web.browser', 'web.cordova'). You can
+ * specify multiple architectures by passing in an array, for example
+ * `['web.cordova', 'os.linux']`.
+ */
+ addAssets(paths, arch) {
+ if(!arch) {
+ buildmessage.error('addAssets requires a second argument specifying ' +
+ 'where the asset should be available. For example: "client", ' +
+ '"server", or ["client", "server"].', { useMyCaller: true });
+ return;
+ }
+
+ // Watch out - we rely on the levels of stack traces inside this
+ // function so don't wrap it in another function without changing that logic
+ this._addFiles("assets", paths, arch);
+ },
+
+ /**
+ * Internal method used by addFiles and addAssets.
+ */
+ _addFiles(type, paths, arch, fileOptions) {
+ if (type !== "sources" && type !== "assets") {
+ throw new Error(`Can only handle sources and assets, not '${type}'.`);
+ }
+
var self = this;
paths = toArray(paths);
@@ -334,22 +387,38 @@ _.extend(PackageAPI.prototype, {
var errors = [];
_.each(paths, function (path) {
forAllMatchingArchs(arch, function (a) {
- if (_.has(self.sources[a], path)) {
- errors.push("Duplicate source file: " + path);
+ const filesOfType = self.files[a][type];
+
+ // Check if we have already added a file at this path
+ if (_.has(filesOfType, path)) {
+ // We want the singular form of the file type
+ const typeName = {
+ sources: 'source',
+ assets: 'asset'
+ }[type];
+
+ errors.push(`Duplicate ${typeName} file: ${path}`);
return;
}
- var source = {relPath: path};
- if (fileOptions)
+
+ const source = {
+ relPath: path
+ };
+
+ if (fileOptions) {
source.fileOptions = fileOptions;
- self.sources[a][path] = source;
+ }
+
+ filesOfType[path] = source;
});
});
// Spit out all the errors at the end, where the number of stack frames to
- // skip is just 1 instead of something like 7 from forAllMatchingArchs and
- // _.each. Avoid using _.each here to keep stack predictable.
+ // skip is just 2 (this function and its callers) instead of something like
+ // 7 from forAllMatchingArchs and _.each. Avoid using _.each here to keep
+ // stack predictable.
for (var i = 0; i < errors.length; ++i) {
- buildmessage.error(errors[i], { useMyCaller: true });
+ buildmessage.error(errors[i], { useMyCaller: 1 });
}
},
diff --git a/tools/isobuild/package-source.js b/tools/isobuild/package-source.js
index ef45a8f78b..be6c3ec061 100644
--- a/tools/isobuild/package-source.js
+++ b/tools/isobuild/package-source.js
@@ -184,7 +184,7 @@ var getExcerptFromReadme = function (text) {
// - arch [required]
// - uses
// - implies
-// - getSourcesFunc
+// - getFiles
// - declaredExports
// - watchSet
//
@@ -238,9 +238,9 @@ var SourceArch = function (pkg, options) {
// unordered and weak are not allowed).
self.implies = options.implies || [];
- // A function that returns the source files for this architecture. Array of
- // objects with keys "relPath" and "fileOptions". Null if loaded from
- // isopack.
+ // A function that returns the source files for this architecture. Object with
+ // keys `sources` and `assets`, where each is an array of objects with keys
+ // "relPath" and "fileOptions". Null if loaded from isopack.
//
// fileOptions is optional and represents arbitrary options passed
// to "api.addFiles"; they are made available on to the plugin as
@@ -251,7 +251,7 @@ var SourceArch = function (pkg, options) {
// plugins in order to compute the sources list, so we have to wait
// until build time (after we have loaded any plugins, including
// local plugins in this package) to compute this.
- self.getSourcesFunc = options.getSourcesFunc || null;
+ self.getFiles = options.getFiles || null;
// Symbols that this architecture should export. List of symbols (as
// strings).
@@ -395,7 +395,9 @@ _.extend(PackageSource.prototype, {
// - sourceRoot (required if sources present)
// - serveRoot (required if sources present)
// - use
- // - sources (array of paths or relPath/fileOptions objects)
+ // - sources (array of paths or relPath/fileOptions objects), note that this
+ // doesn't support assets at this time. If you want to pass assets here, you
+ // should add a new option to this function called `assets`.
// - npmDependencies
// - cordovaDependencies
// - npmDir
@@ -414,7 +416,6 @@ _.extend(PackageSource.prototype, {
// serveRoot is actually a part of a url path, root here is a forward slash
self.serveRoot = options.serveRoot || '/';
- var nodeModulesPath = null;
utils.ensureOnlyExactVersions(options.npmDependencies);
self.npmDependencies = options.npmDependencies;
self.npmCacheDirectory = options.npmDir;
@@ -422,18 +423,25 @@ _.extend(PackageSource.prototype, {
utils.ensureOnlyExactVersions(options.cordovaDependencies);
self.cordovaDependencies = options.cordovaDependencies;
- var sources = _.map(options.sources, function (source) {
- if (typeof source === "string")
- return {relPath: source};
+ const sources = options.sources.map((source) => {
+ if (typeof source === "string") {
+ return {
+ relPath: source
+ };
+ }
+
return source;
});
- var sourceArch = new SourceArch(self, {
+ const sourceArch = new SourceArch(self, {
kind: options.kind,
arch: "os",
uses: _.map(options.use, splitConstraint),
- getSourcesFunc: function () { return sources; },
- nodeModulesPath: nodeModulesPath
+ getFiles() {
+ return {
+ sources: sources
+ }
+ }
});
self.architectures.push(sourceArch);
@@ -1043,13 +1051,17 @@ _.extend(PackageSource.prototype, {
console.log(e.stack); // XXX should we keep this here -- or do we want broken
// packages to fail silently?
buildmessage.exception(e);
+
// Recover by ignoring all of the source files in the
// packages and any remaining handlers. It violates the
// principle of least surprise to half-run a handler
// and then continue.
- api.sources = {};
+ api.files = {};
_.each(compiler.ALL_ARCHES, function (arch) {
- api.sources[arch] = {};
+ api.files[arch] = {
+ sources: [],
+ assets: []
+ };
});
fileAndDepLoader = null;
@@ -1187,7 +1199,9 @@ _.extend(PackageSource.prototype, {
arch: arch,
uses: api.uses[arch],
implies: api.implies[arch],
- getSourcesFunc: function () { return _.values(api.sources[arch]); },
+ getFiles: function () {
+ return api.files[arch];
+ },
declaredExports: api.exports[arch],
watchSet: watchSet
}));
@@ -1247,7 +1261,7 @@ _.extend(PackageSource.prototype, {
sourceArch.watchSet.merge(projectWatchSet);
// Determine source files
- sourceArch.getSourcesFunc = (sourceProcessorSet, watchSet) => {
+ sourceArch.getFiles = (sourceProcessorSet, watchSet) => {
const sourceReadOptions =
sourceProcessorSet.appReadDirectoryOptions(arch);
// Ignore files starting with dot (unless they are explicitly in
@@ -1361,6 +1375,8 @@ _.extend(PackageSource.prototype, {
const assetDir = archinfo.matches(arch, "web") ? "public/" : "private/";
var assetDirs = readAndWatchDirectory('', {names: [assetDir]});
+ const assets = [];
+
if (!_.isEmpty(assetDirs)) {
if (!_.isEqual(assetDirs, [assetDir]))
throw new Error("Surprising assetDirs: " + JSON.stringify(assetDirs));
@@ -1386,18 +1402,18 @@ _.extend(PackageSource.prototype, {
assetDirs.push(item);
} else {
// This file is an asset.
- sources.push({
- relPath: item,
- fileOptions: {
- isAsset: true
- }
+ assets.push({
+ relPath: item
});
}
});
}
}
- return sources;
+ return {
+ sources,
+ assets
+ };
};
});
diff --git a/tools/packaging/release.js b/tools/packaging/release.js
index 92d41c861d..869691916f 100644
--- a/tools/packaging/release.js
+++ b/tools/packaging/release.js
@@ -35,6 +35,10 @@ _.extend(Release.prototype, {
return this.name === null;
},
+ isRecommended: function () {
+ return this._manifest.recommended;
+ },
+
getReleaseTrack: function () {
var self = this;
if (! self.isProperRelease())
diff --git a/tools/runners/run-app.js b/tools/runners/run-app.js
index 871cd5acb0..f885fe6f47 100644
--- a/tools/runners/run-app.js
+++ b/tools/runners/run-app.js
@@ -443,7 +443,7 @@ _.extend(AppRunner.prototype, {
_runOnce: function (options) {
var self = this;
options = options || {};
- const firstRun = options.firstRun;
+ var firstRun = options.firstRun;
Console.enableProgressDisplay(true);
@@ -596,6 +596,8 @@ _.extend(AppRunner.prototype, {
return bundleResultOrRunResult.runResult;
bundleResult = bundleResultOrRunResult.bundleResult;
+ firstRun = false;
+
// Read the settings file, if any
var settings = null;
var settingsWatchSet = new watch.WatchSet;
@@ -627,28 +629,16 @@ _.extend(AppRunner.prototype, {
serverWatchSet = combinedWatchSetForBundleResult(bundleResult);
}
- // Atomically (1) see if we've been stop()'d, (2) if not, create a
- // future that can be used to stop() us once we start running.
- if (self.exitFuture)
- return { outcome: 'stopped' };
- if (self.runFuture)
- throw new Error("already have future?");
- var runFuture = self.runFuture = new Future;
-
const cordovaRunner = self.cordovaRunner;
- // Run Cordova apps
if (cordovaRunner) {
- const plugins =
- cordova.pluginsFromStarManifest(bundleResult.starManifest);
+ const pluginVersions =
+ cordova.pluginVersionsFromStarManifest(bundleResult.starManifest);
if (!cordovaRunner.started) {
const { settingsFile, mobileServerUrl } = self;
const messages = buildmessage.capture(() => {
- cordovaRunner.prepareProject(bundlePath, plugins,
+ cordovaRunner.prepareProject(bundlePath, pluginVersions,
{ settingsFile, mobileServerUrl });
-
- cordovaRunner.printWarningsIfNeeded();
- cordovaRunner.startRunTargets();
});
if (messages.hasMessages()) {
@@ -658,6 +648,7 @@ _.extend(AppRunner.prototype, {
watchSet: combinedWatchSetForBundleResult(bundleResult)
};
}
+ cordovaRunner.printWarningsIfNeeded();
} else {
// If the set of Cordova platforms or plugins changes from one run
// to the next, we just exit, because we don't yet have a way to,
@@ -668,12 +659,20 @@ _.extend(AppRunner.prototype, {
return { outcome: 'outdated-cordova-platforms' };
}
- if (cordovaRunner.havePluginsChangedSinceLastRun(plugins)) {
+ if (cordovaRunner.havePluginsChangedSinceLastRun(pluginVersions)) {
return { outcome: 'outdated-cordova-plugins' };
}
}
}
+ // Atomically (1) see if we've been stop()'d, (2) if not, create a
+ // future that can be used to stop() us once we start running.
+ if (self.exitFuture)
+ return { outcome: 'stopped' };
+ if (self.runFuture)
+ throw new Error("already have future?");
+ var runFuture = self.runFuture = new Future;
+
// Run the program
options.beforeRun && options.beforeRun();
var appProcess = new AppProcess({
@@ -732,6 +731,10 @@ _.extend(AppRunner.prototype, {
}
maybePrintLintWarnings(bundleResult);
+ if (cordovaRunner && !cordovaRunner.started) {
+ cordovaRunner.startRunTargets();
+ }
+
// Start watching for changes for files if requested. There's no
// hurry to do this, since clientWatchSet contains a snapshot of the
// state of the world at the time of bundling, in the form of
diff --git a/tools/runners/run-proxy.js b/tools/runners/run-proxy.js
index 250bbacfe7..fea3896c61 100644
--- a/tools/runners/run-proxy.js
+++ b/tools/runners/run-proxy.js
@@ -172,16 +172,7 @@ _.extend(Proxy.prototype, {
var c = self.httpQueue.shift();
if (self.mode === "errorpage") {
- // XXX serve an app that shows the logs nicely and that also
- // knows how to reload when the server comes back up
- c.res.writeHead(200, {'Content-Type': 'text/plain'});
- c.res.write("Your app is crashing. Here's the latest log.\n\n");
-
- _.each(runLog.getLog(), function (item) {
- c.res.write(item.message + "\n");
- });
-
- c.res.end();
+ showErrorPage(c.res);
} else {
self.proxy.web(c.req, c.res, {
target: 'http://' + self.proxyToHost + ':' + self.proxyToPort
@@ -214,4 +205,59 @@ _.extend(Proxy.prototype, {
}
});
+function showErrorPage(res) {
+ // XXX serve an app that shows the logs nicely and that also
+ // knows how to reload when the server comes back up
+ res.writeHead(200, {'Content-Type': 'text/html'});
+ res.write(`
+
+
+
+ App crashing
+
+
+
+
+
+
+`)
+
+ res.end();
+}
+
+// Copied from packages/blaze/preamble.js
+function escapeEntities(str) {
+ const escapeMap = {
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "'": "'",
+ "`": "`", /* IE allows backtick-delimited attributes?? */
+ "&": "&"
+ };
+
+ const escapeChar = function(c) {
+ return escapeMap[c];
+ };
+
+ return str.replace(/[&<>"'`]/g, escapeChar);
+}
+
exports.Proxy = Proxy;
diff --git a/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/.gitignore b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/.gitignore
new file mode 100644
index 0000000000..4083037423
--- /dev/null
+++ b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/.gitignore
@@ -0,0 +1 @@
+local
diff --git a/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/packages b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/packages
new file mode 100644
index 0000000000..e66ff7806c
--- /dev/null
+++ b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/packages
@@ -0,0 +1,9 @@
+# Meteor packages used by this project, one per line.
+# Check this file (and the other files in this directory) into your repository.
+#
+# 'meteor add' and 'meteor remove' will edit this file for you,
+# but you can also edit it by hand.
+
+meteor-base
+asset-and-source
+ecmascript
diff --git a/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/release b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/release
new file mode 100644
index 0000000000..315c635bf3
--- /dev/null
+++ b/tools/tests/apps/compiler-plugin-asset-and-source/.meteor/release
@@ -0,0 +1 @@
+METEOR@1.1.0.3
diff --git a/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/asset-and-source.js b/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/asset-and-source.js
new file mode 100644
index 0000000000..a12a02591e
--- /dev/null
+++ b/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/asset-and-source.js
@@ -0,0 +1,4 @@
+if (Meteor.isServer) {
+ // Printing out my own source code!
+ console.log(Assets.getText("asset-and-source.js"));
+}
diff --git a/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/package.js b/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/package.js
new file mode 100644
index 0000000000..bbf9c7a0e0
--- /dev/null
+++ b/tools/tests/apps/compiler-plugin-asset-and-source/packages/asset-and-source/package.js
@@ -0,0 +1,9 @@
+Package.describe({
+ name: 'asset-and-source',
+ version: '0.0.1'
+});
+
+Package.onUse(function(api) {
+ api.addFiles('asset-and-source.js');
+ api.addAssets('asset-and-source.js', ['client', 'server']);
+});
diff --git a/tools/tests/command-line.js b/tools/tests/command-line.js
index 91d7e59419..9fd55cf18d 100644
--- a/tools/tests/command-line.js
+++ b/tools/tests/command-line.js
@@ -435,16 +435,7 @@ selftest.define("old cli tests (converted)", function () {
// bundle
run = s.run('bundle', 'foo.tar.gz');
- run.expectExit(0);
-
- // untar the tarball, make sure the tar command succeeds, do it only on linux,
- // since on windows it requires messing with 7z failures, different behavior
- // of commands and options, etc
- if (process.platform !== 'win32') {
- var tar_tvzf = utils.execFileSync('tar', ['tvzf', files.pathJoin(s.cwd, 'foo.tar.gz')]);
- selftest.expectTrue(tar_tvzf.success);
- }
- files.unlink(files.pathJoin(s.cwd, 'foo.tar.gz'));
+ run.matchErr(/This command has been deprecated/);
run = s.run('build', '.');
run.expectExit(0);
@@ -501,4 +492,3 @@ selftest.define("old cli tests (converted)", function () {
run.expectExit(0);
files.unlink(files.pathJoin(s.cwd, 'settings.js'));
});
-
diff --git a/tools/tests/compiler-plugins.js b/tools/tests/compiler-plugins.js
index 089524cd87..e1e897b1c5 100644
--- a/tools/tests/compiler-plugins.js
+++ b/tools/tests/compiler-plugins.js
@@ -348,3 +348,78 @@ selftest.define("compiler plugins - compiler addAsset", () => {
run.stop();
});
+
+
+// Test that a package can have a single file that is both source code and an
+// asset
+selftest.define("compiler plugins - addAssets", () => {
+ const s = new Sandbox({ fakeMongo: true });
+
+ s.createApp('myapp', 'compiler-plugin-asset-and-source');
+ s.cd('myapp');
+
+ const run = startRun(s);
+
+ // Test server-side asset.
+ run.match("Printing out my own source code!");
+
+ // Test client-side asset.
+ const body = getUrl('http://localhost:3000/packages/' +
+ 'asset-and-source/asset-and-source.js');
+ selftest.expectTrue(body.indexOf('Printing out my own source code!') !== -1);
+
+ // Test error messages for malformed package files
+ s.write("packages/asset-and-source/package.js", `Package.describe({
+ name: 'asset-and-source',
+ version: '0.0.1'
+ });
+
+ Package.onUse(function(api) {
+ api.addFiles('asset-and-source.js');
+ api.addAssets('asset-and-source.js', ['client', 'server']);
+ api.addFiles('asset-and-source.js');
+ });
+`);
+
+ run.match(/Duplicate source file/);
+
+ s.write("packages/asset-and-source/package.js", `Package.describe({
+ name: 'asset-and-source',
+ version: '0.0.1'
+ });
+
+ Package.onUse(function(api) {
+ api.addFiles('asset-and-source.js');
+ api.addAssets('asset-and-source.js', ['client', 'server']);
+ api.addAssets('asset-and-source.js', ['client', 'server']);
+ });
+`);
+
+ run.match(/Duplicate asset file/);
+
+ s.write("packages/asset-and-source/package.js", `Package.describe({
+ name: 'asset-and-source',
+ version: '0.0.1'
+ });
+
+ Package.onUse(function(api) {
+ api.addFiles('asset-and-source.js', 'client', { isAsset: true });
+ });
+ `);
+
+ run.match(/deprecated/);
+
+ s.write("packages/asset-and-source/package.js", `Package.describe({
+ name: 'asset-and-source',
+ version: '0.0.1'
+ });
+
+ Package.onUse(function(api) {
+ api.addAssets('asset-and-source.js');
+ });
+`);
+
+ run.match(/requires a second argument/);
+
+ run.stop();
+});
diff --git a/tools/tests/cordova-windows-errors.js b/tools/tests/cordova-windows-errors.js
index 93056fd183..6350b6f77d 100644
--- a/tools/tests/cordova-windows-errors.js
+++ b/tools/tests/cordova-windows-errors.js
@@ -4,7 +4,7 @@ var _ = require('underscore');
var Sandbox = selftest.Sandbox;
-selftest.define("windows prints correct message when it can't do mobile things - add-platform, install-sdk, configure-android, android-launch", ["windows"], function () {
+selftest.define("windows prints correct message when it can't do mobile things - add-platform, install-sdk, configure-android", ["windows"], function () {
var s = new Sandbox();
var run;
@@ -18,7 +18,7 @@ selftest.define("windows prints correct message when it can't do mobile things -
run.matchErr("Windows");
});
});
- _.each(['configure-android', 'android-launch'], function (command) {
+ _.each(['configure-android'], function (command) {
run = s.run(command);
run.matchErr("Windows");
});
diff --git a/tools/tests/create.js b/tools/tests/create.js
index 4b3902a17e..d3f7cb83c1 100644
--- a/tools/tests/create.js
+++ b/tools/tests/create.js
@@ -6,8 +6,8 @@ selftest.define("create", function () {
// Can we create an app? Yes!
var run = s.run("create", "foobar");
- run.waitSecs(15);
- run.match("foobar: created");
+ run.waitSecs(60);
+ run.match("Created a new Meteor app in 'foobar'.");
run.match("To run your new app");
run.expectExit(0);
diff --git a/tools/tests/old/app-with-private/packages/test-package/package.js b/tools/tests/old/app-with-private/packages/test-package/package.js
index ade8b0caea..5f3572453d 100644
--- a/tools/tests/old/app-with-private/packages/test-package/package.js
+++ b/tools/tests/old/app-with-private/packages/test-package/package.js
@@ -11,5 +11,5 @@ Package.registerBuildPlugin({
Package.onUse(function (api) {
api.export('TestAsset', 'server');
api.addFiles(['test-package.js', 'test-package.txt'],'server');
- api.addFiles(['test.notregistered'], 'server', {isAsset: true});
+ api.addAssets(['test.notregistered'], 'server');
});
diff --git a/tools/tests/parse-stack-test.js b/tools/tests/parse-stack-test.js
index 920eca8339..0c804e7585 100644
--- a/tools/tests/parse-stack-test.js
+++ b/tools/tests/parse-stack-test.js
@@ -3,15 +3,17 @@ import { parse, markBottom } from '../utils/parse-stack.js';
import _ from 'underscore';
import Fiber from 'fibers';
import Future from 'fibers/future';
+import files from '../fs/files.js';
selftest.define("parse-stack - parse stack traces without fibers", () => {
const err = new Error();
const parsedStack = parse(err).outsideFiber;
- selftest.expectEqual(_.last(parsedStack[0].file.split("/")),
- "parse-stack-test.js");
- selftest.expectEqual(_.last(_.last(parsedStack).file.split("/")),
- "main.js");
+ const firstFilePath = files.convertToStandardPath(parsedStack[0].file);
+ selftest.expectEqual(_.last(firstFilePath.split("/")), "parse-stack-test.js");
+
+ const lastFilePath = files.convertToStandardPath(_.last(parsedStack).file);
+ selftest.expectEqual(_.last(lastFilePath.split("/")), "main.js");
markBottom(() => {
const markedErr = new Error();
@@ -27,7 +29,10 @@ selftest.define("parse-stack - parse stack traces without fibers", () => {
// The stack trace should only contain this one function since we marked the
// bottom
selftest.expectEqual(outsideFiber.length, 1);
- selftest.expectEqual(_.last(outsideFiber[0].file.split("/")),
+
+ const firstOutsideFiberPath =
+ files.convertToStandardPath(outsideFiber[0].file);
+ selftest.expectEqual(_.last(firstOutsideFiberPath.split("/")),
"parse-stack-test.js");
})();
});
diff --git a/tools/tool-env/install-git-hooks.js b/tools/tool-env/install-git-hooks.js
index 7388a0dd98..adc596f41e 100644
--- a/tools/tool-env/install-git-hooks.js
+++ b/tools/tool-env/install-git-hooks.js
@@ -32,6 +32,9 @@ const METEOR_HOOK_SUFFIX = '.meteor-hook';
const METEOR_HOOK_LINE_REGEX = /.*meteor-hook.*/;
export default function installGitHooks() {
+ // ensure the hooks folder is present
+ files.mkdir_p(hookDestination);
+
allPossibleHooks.map((hookName) => {
if (!files.exists(hookDestination)) {
// Don't do anything if the hook destination does not exist, eg.,
diff --git a/tools/tool-testing/selftest.js b/tools/tool-testing/selftest.js
index 92b6725c78..72c20320aa 100644
--- a/tools/tool-testing/selftest.js
+++ b/tools/tool-testing/selftest.js
@@ -22,6 +22,9 @@ var tropohouseModule = require('../packaging/tropohouse.js');
var packageMapModule = require('../packaging/package-map.js');
var release = require('../packaging/release.js');
+var projectContextModule = require('../project-context.js');
+var upgraders = require('../upgraders.js');
+
// Exception representing a test failure
var TestFailure = function (reason, details) {
var self = this;
@@ -607,10 +610,10 @@ _.extend(Sandbox.prototype, {
createApp: function (to, template, options) {
var self = this;
options = options || {};
+ var absoluteTo = files.pathJoin(self.cwd, to);
files.cp_r(files.pathJoin(
files.convertToStandardPath(__dirname), '..', 'tests', 'apps', template),
- files.pathJoin(self.cwd, to),
- { ignore: [/^local$/] });
+ absoluteTo, { ignore: [/^local$/] });
// If the test isn't explicitly managing a mock warehouse, ensure that apps
// run with our release by default.
if (options.release) {
@@ -619,6 +622,14 @@ _.extend(Sandbox.prototype, {
self.write(files.pathJoin(to, '.meteor/release'), release.current.name);
}
+ // Make sure the apps don't run any upgraders, unless they intentionally
+ // have a partial upgraders file
+ var upgradersFile =
+ new projectContextModule.FinishedUpgraders({projectDir: absoluteTo});
+ if (_.isEmpty(upgradersFile.readUpgraders())) {
+ upgradersFile.appendUpgraders(upgraders.allUpgraders());
+ }
+
if (options.dontPrepareApp)
return;
@@ -1582,6 +1593,9 @@ var getFilteredTests = function (options) {
} else if (options.onlyChanged &&
test.fileHash === testState.lastPassedHashes[test.file]) {
newTags.push('unchanged');
+ } else if (options.excludeRegexp &&
+ options.excludeRegexp.test(test.name)) {
+ newTags.push('excluded');
}
// We make sure to not run galaxy tests unless the user explicitly asks us
@@ -1606,6 +1620,9 @@ var getFilteredTests = function (options) {
if (options.testRegexp) {
tagsToSkip.push('non-matching');
}
+ if (options.excludeRegexp) {
+ tagsToSkip.push('excluded');
+ }
if (options.onlyChanged) {
tagsToSkip.push('unchanged');
}
diff --git a/tools/upgraders.js b/tools/upgraders.js
index 67c5241f12..db042ed124 100644
--- a/tools/upgraders.js
+++ b/tools/upgraders.js
@@ -4,6 +4,7 @@ var _ = require('underscore');
var files = require('./fs/files.js');
var Console = require('./console/console.js').Console;
import main from './cli/main.js';
+import buildmessage from './utils/buildmessage.js';
import * as cordova from './cordova';
// This file implements "upgraders" --- functions which upgrade a Meteor app to
@@ -22,9 +23,9 @@ var printedNoticeHeaderThisProcess = false;
var maybePrintNoticeHeader = function () {
if (printedNoticeHeaderThisProcess)
return;
- console.log();
- console.log("-- Notice --");
- console.log();
+ Console.info();
+ Console.info("-- Notice --");
+ Console.info();
printedNoticeHeaderThisProcess = true;
};
@@ -174,11 +175,17 @@ var upgradersByName = {
'cordova-build'));
// Cordova plugin IDs have changed as part of moving to npm, so we convert
- // old plugin IDs to new IDs
- if (files.exists(projectContext.cordovaPluginsFile.filename)) {
- let pluginVersions = projectContext.cordovaPluginsFile.getPluginVersions();
- pluginVersions = cordova.convertPluginVersionsToNewIDs(pluginVersions);
- projectContext.cordovaPluginsFile.write(pluginVersions);
+ // old plugin IDs to new IDs. We also convert old-style GitHub tarball URLs
+ // to new Git URLs, and check if other Git URLs contain a SHA reference.
+ const pluginsFile = projectContext.cordovaPluginsFile;
+ let messages;
+ if (files.exists(pluginsFile.filename)) {
+ messages = buildmessage.capture(
+ { title: `converting Cordova plugins` }, () => {
+ let pluginVersions = pluginsFile.getPluginVersions();
+ pluginVersions = cordova.convertPluginVersions(pluginVersions);
+ pluginsFile.write(pluginVersions);
+ });
}
// Don't display notice if the project has no Cordova platforms added
@@ -186,28 +193,22 @@ var upgradersByName = {
maybePrintNoticeHeader();
+ // Print error messages generated during plugin conversion, if any
+ if (messages && messages.hasMessages()) {
+ Console.printMessages(messages);
+ }
+ },
+
+ "1.2.0-breaking-changes": function () {
+ maybePrintNoticeHeader();
Console.info(
-`Meteor 1.2 includes various changes to the Cordova integration.
-
-Cordova tools, platforms and plugins have been updated to the latest versions. \
-This may require you to make changes to your app. For details, see the Cordova \
-release notes for for the different versions:`,
-Console.url('https://cordova.apache.org/#news'), `
-
-As part of moving to npm, many Cordova plugins have been renamed. Meteor should \
-perform conversions automatically, but you may want to be aware of this to \
-avoid surprises. See`,
-Console.url('https://cordova.apache.org/announcements/2015/04/21/plugins-release-and-move-to-npm.html'),
-`for more information.
-
-The bundled Android tools have been removed and a system-wide install of the \
-Android SDK is now required. This should make it easier to keep the development \
-toolchain up to date and helps avoid some difficult to diagnose failures.
-If you don't have your own Android tools installed already, you can find \
-more information about installing the Android SDK for your platform here:`,
-Console.url(cordova.installationInstructionsUrlForPlatform('android')),
- Console.options({ bulletPoint: "1.2.0: " }));
- }
+`Meteor 1.2 includes many changes and improvements to the build system, \
+some of which might require small changes to apps and packages. Please read \
+the guide about breaking changes here:`,
+ Console.url("https://github.com/meteor/meteor/wiki/Breaking-changes-in-Meteor-1.2"),
+ Console.options({ bulletPoint: "1.2: " })
+ );
+ },
////////////
// PLEASE. When adding new upgraders that print mesasges, follow the
diff --git a/tools/utils/buildmessage.js b/tools/utils/buildmessage.js
index cbfda41c61..7e72f44f6c 100644
--- a/tools/utils/buildmessage.js
+++ b/tools/utils/buildmessage.js
@@ -486,7 +486,11 @@ var exception = function (error) {
column: error.column
});
} else {
- var stack = parseStack.parse(error).outsideFiber;
+ var parsed = parseStack.parse(error);
+
+ // If there is a part inside the fiber, that's the one we want. Otherwise,
+ // use the one outside.
+ var stack = parsed.insideFiber || parsed.outsideFiber;
var locus = stack[0];
currentJob.get().addMessage({
message: message,
diff --git a/tools/utils/processes.js b/tools/utils/processes.js
index 79d2185027..b569b7a8d8 100644
--- a/tools/utils/processes.js
+++ b/tools/utils/processes.js
@@ -56,8 +56,8 @@ export function execFileAsync(command, args,
options = { waitForClose: true }) {
// args is optional, so if it's not an array we interpret it as options
if (!Array.isArray(args)) {
- args = [];
options = _.extend(options, args);
+ args = [];
}
// The child process close event is emitted when the stdio streams