diff --git a/packages/constraint-solver/constraint-solver.js b/packages/constraint-solver/constraint-solver.js index 3978cb5c2d..e314232f84 100644 --- a/packages/constraint-solver/constraint-solver.js +++ b/packages/constraint-solver/constraint-solver.js @@ -37,9 +37,9 @@ ConstraintSolver.PackagesResolver = function (catalog, options) { forEveryVersion(function (packageName, version, versionDef) { var unibuilds = {}; // XXX in theory there might be different archs but in practice they are - // always "os" and "browser". Fix this once we actually have different + // always "os" and "client". Fix this once we actually have different // archs used. - _.each(["os", "browser"], function (arch) { + _.each(["os", "client", "client.browser", "client.test"], function (arch) { var unitName = packageName + "#" + arch; unibuilds[unitName] = new ConstraintSolver.UnitVersion( unitName, version, versionDef.earliestCompatibleVersion); @@ -128,10 +128,10 @@ ConstraintSolver.PackagesResolver.prototype.resolve = } // split every package name to one or more archs belonging to that package - // (["foobar"] => ["foobar#os", "foobar#browser"]) - // XXX for now just put #os and #browser + // (["foobar"] => ["foobar#os", "foobar#client"]) + // XXX for now just put #os and #client options.upgrade = _.filter(_.flatten(_.map(options.upgrade, function (packageName) { - return [packageName + "#os", packageName + "#browser"]; + return [packageName + "#os", packageName + "#client"]; })), _.identity); var dc = self._splitDepsToConstraints(dependencies, constraints); @@ -162,7 +162,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = var resultChoices = {}; _.each(res, function (uv) { // Since we don't yet define the interface for a an app to depend only on - // certain unibuilds of the packages (like only browser unibuilds) and we know + // certain unibuilds of the packages (like only client unibuilds) and we know // that each unibuild weakly depends on other sibling unibuilds of the same // version, we can safely output the whole package for each unibuild in the // result. @@ -198,7 +198,7 @@ ConstraintSolver.PackagesResolver.prototype.propagateExactDeps = }; // takes dependencies and constraints and rewrites the names from "foo" to -// "foo#os" and "foo#browser" +// "foo#os" and "foo#client" // XXX right now creates a dependency for every unibuild it can find ConstraintSolver.PackagesResolver.prototype._splitDepsToConstraints = function (inputDeps, inputConstraints) { @@ -233,8 +233,8 @@ ConstraintSolver.PackagesResolver.prototype._unibuildsForPackage = var self = this; var unibuildPrefix = packageName + "#"; var unibuilds = []; - // XXX hardcode os and browser - _.each(["os", "browser"], function (arch) { + // XXX hardcode os and client + _.each(["os", "client", "client.browser", "client.test"], function (arch) { if (self.resolver.unitsVersions[unibuildPrefix + arch]) unibuilds.push(unibuildPrefix + arch); }); diff --git a/packages/meteor/plugin/basic-file-types.js b/packages/meteor/plugin/basic-file-types.js index 3c87328198..31d31dcb35 100644 --- a/packages/meteor/plugin/basic-file-types.js +++ b/packages/meteor/plugin/basic-file-types.js @@ -4,7 +4,7 @@ Plugin.registerSourceHandler("css", function (compileStep) { // XXX annoying that this is replicated in .css, .less, and .styl - if (! compileStep.archMatches('browser')) { + if (! compileStep.archMatches('client')) { // XXX in the future, might be better to emit some kind of a // warning if a stylesheet is included on the server, rather than // silently ignoring it. but that would mean you can't stick .css diff --git a/packages/templating/plugin/compile-templates.js b/packages/templating/plugin/compile-templates.js index b364737c2e..f1148cbac6 100644 --- a/packages/templating/plugin/compile-templates.js +++ b/packages/templating/plugin/compile-templates.js @@ -2,8 +2,7 @@ var path = Npm.require('path'); var doHTMLScanning = function (compileStep, htmlScanner) { - // XXX use archinfo rather than rolling our own - if (! compileStep.arch.match(/^browser(\.|$)/)) + if (! compileStep.archMatches("client")) // XXX might be nice to throw an error here, but then we'd have to // make it so that packages.js ignores html files that appear in // the server directories in an app tree.. or, it might be nice to diff --git a/tools/commands.js b/tools/commands.js index 2802fb44c5..077dac72fc 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -492,7 +492,8 @@ main.registerCommand({ directory: { type: Boolean }, architecture: { type: String }, // Undocumented - 'for-deploy': { type: Boolean } + 'for-deploy': { type: Boolean }, + 'client-arch': { type: String } } }, function (options) { // XXX if they pass a file that doesn't end in .tar.gz or .tgz, add @@ -537,7 +538,9 @@ main.registerCommand({ // default? i guess the problem with using DEPLOY_ARCH as default // is then 'meteor bundle' with no args fails if you have any local // packages with binary npm dependencies - arch: bundleArch + arch: bundleArch, + clientArchs: options['client-arch'] ? [options['client-arch']] + : ['client.browser'] } }); if (bundleResult.errors) { diff --git a/tools/compiler.js b/tools/compiler.js index f1e3a8ed73..13cb48abc0 100644 --- a/tools/compiler.js +++ b/tools/compiler.js @@ -67,6 +67,7 @@ compiler.eachUsedUnibuild = function ( callback = options; options = {}; } + var acceptableWeakPackages = options.acceptableWeakPackages || {}; var processedBuildId = {}; @@ -81,6 +82,7 @@ compiler.eachUsedUnibuild = function ( while (!_.isEmpty(usesToProcess)) { var use = usesToProcess.shift(); + console.log(use); var unibuild = packageLoader.getUnibuild(use.package, arch); if (!unibuild) { @@ -448,11 +450,11 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // information. // - pathForSourceMap: If this file is to be included in a source map, // this is the name you should use for it in the map. - // - rootOutputPath: on browser targets, for resources such as + // - rootOutputPath: on client targets, for resources such as // stylesheet and static assets, this is the root URL that // will get prepended to the paths you pick for your output // files so that you get your own namespace, for example - // '/packages/foo'. null on non-browser targets + // '/packages/foo'. null on non-client targets // - fileOptions: any options passed to "api.add_files"; for // use by the plugin. The built-in "js" plugin uses the "bare" // option for files that shouldn't be wrapped in a closure. @@ -464,11 +466,11 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // file as a Buffer. If n is omitted you get the rest of the // file. // - appendDocument({ section: "head", data: "my markup" }) - // Browser targets only. Add markup to the "head" or "body" + // Client targets only. Add markup to the "head" or "body" // section of the document. // - addStylesheet({ path: "my/stylesheet.css", data: "my css", // sourceMap: "stringified json sourcemap"}) - // Browser targets only. Add a stylesheet to the + // Client targets only. Add a stylesheet to the // document. 'path' is a requested URL for the stylesheet that // may or may not ultimately be honored. (Meteor will add // appropriate tags to cause the stylesheet to be loaded. It @@ -563,9 +565,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, return ret; }, appendDocument: function (options) { - if (! archinfo.matches(inputSourceArch.arch, "browser")) + if (! archinfo.matches(inputSourceArch.arch, "client")) throw new Error("Document sections can only be emitted to " + - "browser targets"); + "client targets"); if (options.section !== "head" && options.section !== "body") throw new Error("'section' must be 'head' or 'body'"); if (typeof options.data !== "string") @@ -577,9 +579,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, }); }, addStylesheet: function (options) { - if (! archinfo.matches(inputSourceArch.arch, "browser")) + if (! archinfo.matches(inputSourceArch.arch, "client")) throw new Error("Stylesheets can only be emitted to " + - "browser targets"); + "client targets"); if (typeof options.data !== "string") throw new Error("'data' option to addStylesheet must be a string"); sourceIsWatched = true; @@ -596,8 +598,8 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, throw new Error("'data' option to addJavaScript must be a string"); if (typeof options.sourcePath !== "string") throw new Error("'sourcePath' option must be supplied to addJavaScript. Consider passing inputPath."); - if (options.bare && ! archinfo.matches(inputSourceArch.arch, "browser")) - throw new Error("'bare' option may only be used for browser targets"); + if (options.bare && ! archinfo.matches(inputSourceArch.arch, "client")) + throw new Error("'bare' option may only be used for client targets"); sourceIsWatched = true; js.push({ source: options.data, diff --git a/tools/package-loader.js b/tools/package-loader.js index 55d41ccef4..2fdb5ce855 100644 --- a/tools/package-loader.js +++ b/tools/package-loader.js @@ -40,6 +40,7 @@ _.extend(exports.PackageLoader.prototype, { if (options.throwOnError === undefined) { options.throwOnError = true; } + var loadPath = self.getLoadPathForPackage(name); if (! loadPath) { if (options.throwOnError === false) diff --git a/tools/package-source.js b/tools/package-source.js index c544c9d0b6..4306bd9e79 100644 --- a/tools/package-source.js +++ b/tools/package-source.js @@ -667,22 +667,29 @@ _.extend(PackageSource.prototype, { buildmessage.error("Package name invalid: " + self.name); } + var allWheres = ['server', 'client']; + var allWhereObj = {}; + _.each(allWheres, function (where) { + allWhereObj[where] = []; + }); + // source files used - var sources = {client: [], server: []}; + var sources = _.clone(allWhereObj); // symbols exported - var exports = {client: [], server: []}; + var exports = _.clone(allWhereObj); // packages used and implied (keys are 'package', 'unordered', and // 'weak'). an "implied" package is a package that will be used by a unibuild // which uses us. - var uses = {client: [], server: []}; - var implies = {client: [], server: []}; + var uses = _.clone(allWhereObj); + var implies = _.clone(allWhereObj); // For this old-style, on_use/on_test/where-based package, figure // out its dependencies by calling its on_xxx functions and seeing // what it does. // + // XXX fix this comment // We have a simple strategy. Call its on_xxx handler with no // 'where', which is what happens when the package is added // directly to an app, and see what files it adds to the client @@ -702,25 +709,23 @@ _.extend(PackageSource.prototype, { return x ? [x] : []; }; - var allWheres = ['client', 'server']; var toWhereArray = function (where) { if (!(where instanceof Array)) { where = where ? [where] : allWheres; } where = _.uniq(where); - var realWhere = _.intersection(where, allWheres); - if (realWhere.length !== where.length) { - var badWheres = _.difference(where, allWheres); - // avoid using _.each so as to not add more frames to skip - for (var i = 0; i < badWheres.length; ++i) { + _.each(where, function (inputWhere) { + var isMatch = _.any(_.map(allWheres, function (actualWhere) { + return archinfo.matches(inputWhere, actualWhere); + })); + if (! isMatch) { buildmessage.error( - "Invalid 'where' argument: '" + badWheres[i] + "'", + "Invalid 'where' argument: '" + indivWhere + "'", // skip toWhereArray in addition to the actual API function {useMyCaller: 1}); - }; - // recover by using the real ones only - } - return realWhere; + } + }); + return where; }; var api = { @@ -728,7 +733,8 @@ _.extend(PackageSource.prototype, { // used. Can also take literal package objects, if you have // anonymous packages you want to use (eg, app packages) // - // @param where 'client', 'server', or an array of those. + // @param where 'client', 'client.browser', 'client.test', 'server', + // or an array of those. // The default is ['client', 'server']. // // options can include: @@ -843,7 +849,7 @@ _.extend(PackageSource.prototype, { // // @param symbols String (eg "Foo") or array of String // @param where 'client', 'server', or an array of those. - // The default is ['client', 'server']. + // The default is ['client', 'client.test', 'client.browser', 'server']. // @param options 'testOnly', boolean. export: function (symbols, where, options) { // Support `api.export("FooTest", {testOnly: true})` without @@ -883,7 +889,7 @@ _.extend(PackageSource.prototype, { // packages and any remaining handlers. It violates the // principle of least surprise to half-run a handler // and then continue. - sources = {client: [], server: []}; + sources = _.clone(allWhereObj); fileAndDepLoader = null; self.pluginInfo = {}; npmDependencies = null; @@ -909,9 +915,9 @@ _.extend(PackageSource.prototype, { // For all implies and uses, fill in the unspecified dependencies from the // release. - _.each(['server', 'client'], function (label) { - uses[label] = _.map(uses[label], setFromRel); - implies[label] = _.map(implies[label], setFromRel); + _.each(allWheres, function (label) { + uses[label] = _.map(uses[label], setFromRel); + implies[label] = _.map(implies[label], setFromRel); }); }; @@ -949,45 +955,47 @@ _.extend(PackageSource.prototype, { }); // Create source architectures, one for the server and one for the client. - _.each(["browser", "os"], function (arch) { - var where = (arch === "browser") ? "client" : "server"; + _.each(["client", "os"], function (arch) { + var sourceWhere = (arch === "client") ? "client" : "server"; + _.each(allWheres, function (where) { + if (archinfo.matches(where, sourceWhere)) { + // Everything depends on the package 'meteor', which sets up + // the basic environment) (except 'meteor' itself, and js-analyze + // which needs to be loaded by the linker). + // XXX add a better API for js-analyze to declare itself here + if (name !== "meteor" && name !== "js-analyze" && + !process.env.NO_METEOR_PACKAGE) { + // Don't add the dependency if one already exists. This allows the + // package to create an unordered dependency and override the one that + // we'd add here. This is necessary to resolve the circular dependency + // between meteor and underscore (underscore has an unordered + // dependency on meteor dating from when the .js extension handler was + // in the "meteor" package). + var alreadyDependsOnMeteor = + !! _.find(uses[where], function (u) { + return u.package === "meteor"; + }); + if (! alreadyDependsOnMeteor) + uses[where].unshift({ package: "meteor" }); + } - // Everything depends on the package 'meteor', which sets up - // the basic environment) (except 'meteor' itself, and js-analyze - // which needs to be loaded by the linker). - // XXX add a better API for js-analyze to declare itself here - if (name !== "meteor" && name !== "js-analyze" && - !process.env.NO_METEOR_PACKAGE) { - // Don't add the dependency if one already exists. This allows the - // package to create an unordered dependency and override the one that - // we'd add here. This is necessary to resolve the circular dependency - // between meteor and underscore (underscore has an unordered - // dependency on meteor dating from when the .js extension handler was - // in the "meteor" package). - var alreadyDependsOnMeteor = - !! _.find(uses[where], function (u) { - return u.package === "meteor"; - }); - if (! alreadyDependsOnMeteor) - uses[where].unshift({ package: "meteor" }); - } - - // Each unibuild has its own separate WatchSet. This is so that, eg, a test - // unibuild's dependencies doesn't end up getting merged into the - // pluginWatchSet of a package that uses it: only the use unibuild's - // dependencies need to go there! - var watchSet = new watch.WatchSet(); - watchSet.addFile(packageJsPath, packageJsHash); - - self.architectures.push(new SourceArch(self, { - name: "main", - arch: arch, - uses: uses[where], - implies: implies[where], - getSourcesFunc: function () { return sources[where]; }, - declaredExports: exports[where], - watchSet: watchSet - })); + // Each unibuild has its own separate WatchSet. This is so that, eg, a test + // unibuild's dependencies doesn't end up getting merged into the + // pluginWatchSet of a package that uses it: only the use unibuild's + // dependencies need to go there! + var watchSet = new watch.WatchSet(); + watchSet.addFile(packageJsPath, packageJsHash); + self.architectures.push(new SourceArch(self, { + name: "main", + arch: arch, + uses: uses[where], + implies: implies[where], + getSourcesFunc: function () { return sources[where]; }, + declaredExports: exports[where], + watchSet: watchSet + })); + } + }); }); // If we have built this before, read the versions that we ended up using. @@ -1034,7 +1042,7 @@ _.extend(PackageSource.prototype, { // Determine used packages var project = require('./project.js').project; var names = project.getConstraints(); - var arch = archName === "server" ? "os" : "browser"; + var arch = archName === "server" ? "os" : "client"; // Create unibuild var sourceArch = new SourceArch(self, { @@ -1185,6 +1193,7 @@ _.extend(PackageSource.prototype, { include: [/.?/], // we DO look under dot directories here exclude: ignoreFiles + }); _.each(assetsAndSubdirs, function (item) { diff --git a/tools/unipackage.js b/tools/unipackage.js index cb5958b3a9..f86b15ad08 100644 --- a/tools/unipackage.js +++ b/tools/unipackage.js @@ -156,7 +156,7 @@ _.extend(Unibuild.prototype, { importStubServePath: isApp && '/packages/global-imports.js', prelinkFiles: self.prelinkFiles, packageVariables: self.packageVariables, - includeSourceMapInstructions: archinfo.matches(self.arch, "browser"), + includeSourceMapInstructions: archinfo.matches(self.arch, "client"), name: self.pkg.name || null }); @@ -305,7 +305,7 @@ _.extend(Unipackage.prototype, { _.pluck(self.unibuilds, 'arch').concat(self._toolArchitectures()) ).sort(); // Ensure that our buildArchitectures string does not look like - // browser+os+os.osx.x86_64 + // client+os+os.osx.x86_64 // This would happen if there is an 'os' unibuild but a platform-specific // tool (eg, in meteor-tool). This would confuse catalog.getBuildsForArches // into thinking that it would work for Linux, since the 'os' means @@ -336,7 +336,7 @@ _.extend(Unipackage.prototype, { }, // Return the unibuild of the package to use for a given target architecture - // (eg, 'os.linux.x86_64' or 'browser'), or throw an exception if that + // (eg, 'os.linux.x86_64' or 'client'), or throw an exception if that // packages can't be loaded under these circumstances. getUnibuildAtArch: function (arch) { var self = this; @@ -623,6 +623,9 @@ _.extend(Unipackage.prototype, { JSON.stringify(resource.type)); }); + if (unibuildMeta.arch === 'browser') + unibuildMeta.arch = 'client'; // XXX hack, figure out how to get this work + self.unibuilds.push(new Unibuild(self, { name: unibuildMeta.name, arch: unibuildMeta.arch,