Separate "browser" target into web.browser/cordova

Cordova projects often have a different set of files than web targets,
so we would like to be able to target different client architectures in
our bundles. Ideally, we allow the user to use arbitrary client
architectures - but this patch is a step in the right direction by
abstracting out more of the hard coded "browser"/"os" lines.

We accomplish this separation in a backwards compatible way by allowing
api.___ commands to target a "client" architecture. For example,
api.addFiles('a.js', 'client') adds 'a.js' to both the 'client.browser'
and 'client.cordova' targets.

Effects on 0.9 packaging stuff: packages don't have to change, but the
"data.json" file in ".meteor0" has "browser" in some places. We think we
have to fix the troposphere code where this data.json is created.

Some plugins will also be backwards-incompatible with this change, since
many have a "clientArch.matches("browser")" line in the plugin
code. Ideally, we fix plugins so that this stops being an issue, but for
now package authors can just patch that line.

At the compiled (unipackage) level the new names are 'web.browser' and
'web.cordova', replacing 'browser'. In package.js, the new names are
'client.browser' and 'client.cordova', serving as an adjunct to 'client'.
This commit is contained in:
Matthew Arbesfeld
2014-07-31 13:48:50 -07:00
committed by David Glasser
parent 06a7dc90b5
commit 8bcbd65344
20 changed files with 227 additions and 149 deletions

View File

@@ -17,7 +17,7 @@ var insertVersion = function (name, version, ecv, deps) {
references: [
{ arch: "os", targetSlice: "main", weak: false,
implied: false, unordered: false },
{ arch: "browser", targetSlice: "main", weak: false,
{ arch: "web", targetSlice: "main", weak: false,
implied: false, unordered: false }]
};
});
@@ -25,7 +25,7 @@ var insertVersion = function (name, version, ecv, deps) {
earliestCompatibleVersion: ecv,
dependencies: constructedDeps });
Builds.insert({ packageName: name, version: version,
buildArchitectures: "browser+os" });
buildArchitectures: "web+os" });
};
insertVersion("sparky-forms", "1.1.2", "1.0.0", {"forms": "=1.0.1", "sparkle": "=2.1.1"});
insertVersion("sparky-forms", "1.0.0", "1.0.0", {"awesome-dropdown": "=1.4.0"});
@@ -505,7 +505,7 @@ function getCatalogStub (gems) {
packageVersion.dependencies[name] = {
constraint: convertConstraints(constraints)[0], // XXX pick first one only
references: [{
"arch": "browser"
"arch": "web"
}, {
"arch": "os" }]
};

View File

@@ -1,5 +1,12 @@
var semver = Npm.require('semver');
// Copied from archinfo.matches() in tools/
var archMatches = function (arch, baseArch) {
return arch.substr(0, baseArch.length) === baseArch &&
(arch.length === baseArch.length ||
arch.substr(baseArch.length, 1) === ".");
};
ConstraintSolver = {};
// catalog is a catalog.Catalog object. We have to pass this in because
@@ -60,9 +67,10 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function (
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
// archs used.
_.each(["os", "browser"], function (arch) {
// always "os", "web.browser" and "web.cordova". Fix this once we
// actually have different archs used.
var allArchs = ["os", "web.browser", "web.cordova"];
_.each(allArchs, function (arch) {
var unitName = packageName + "#" + arch;
unibuilds[unitName] = new ConstraintSolver.UnitVersion(
unitName, version, versionDef.earliestCompatibleVersion);
@@ -73,24 +81,28 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function (
self._ensurePackageInfoLoaded(depName);
_.each(dep.references, function (ref) {
var unitName = packageName + "#" + ref.arch;
var unitVersion = unibuilds[unitName];
_.each(allArchs, function (arch) {
if (archMatches(arch, ref.arch)) {
var unitName = packageName + "#" + arch;
var unitVersion = unibuilds[unitName];
if (! unitVersion)
throw new Error("A non-standard arch " + ref.arch + " for package " + packageName);
if (! unitVersion)
throw new Error("A non-standard arch " + arch + " for package " + packageName);
var targetUnitName = depName + "#" + ref.arch;
var targetUnitName = depName + "#" + arch;
// Add the dependency if needed
if (! ref.weak)
unitVersion.addDependency(targetUnitName);
// Add the dependency if needed
if (! ref.weak)
unitVersion.addDependency(targetUnitName);
// Add a constraint if such exists
if (dep.constraint && dep.constraint !== "none") {
var constraint =
self.resolver.getConstraint(targetUnitName, dep.constraint);
unitVersion.addConstraint(constraint);
}
// Add a constraint if such exists
if (dep.constraint && dep.constraint !== "none") {
var constraint =
self.resolver.getConstraint(targetUnitName, dep.constraint);
unitVersion.addConstraint(constraint);
}
}
});
});
});
@@ -168,10 +180,11 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
}
// 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#web.browser", ...])
// XXX for now just hardcode in all of the known architectures
options.upgrade = _.filter(_.flatten(_.map(options.upgrade, function (packageName) {
return [packageName + "#os", packageName + "#browser"];
return [packageName + "#os", packageName + "#web.browser",
packageName + "#web.cordova"];
})), _.identity);
var dc = self._splitDepsToConstraints(dependencies, constraints);
@@ -202,7 +215,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
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 web 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.
@@ -245,7 +258,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#web.browser" and "foo#web.cordova"
// XXX right now creates a dependency for every unibuild it can find
ConstraintSolver.PackagesResolver.prototype._splitDepsToConstraints =
function (inputDeps, inputConstraints) {
@@ -280,8 +293,9 @@ ConstraintSolver.PackagesResolver.prototype._unibuildsForPackage =
var self = this;
var unibuildPrefix = packageName + "#";
var unibuilds = [];
// XXX hardcode os and browser
_.each(["os", "browser"], function (arch) {
// XXX hardcode all common architectures assuming that every package has the
// same set of architectures.
_.each(["os", "web.browser", "web.cordova"], function (arch) {
if (self.resolver.unitsVersions[unibuildPrefix + arch])
unibuilds.push(unibuildPrefix + arch);
});

View File

@@ -5,7 +5,7 @@ var Future = Npm.require('fibers/future');
Plugin.registerSourceHandler("less", function (compileStep) {
// XXX annoying that this is replicated in .css, .less, and .styl
if (! compileStep.archMatches('browser')) {
if (! compileStep.archMatches('web')) {
// 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

View File

@@ -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('web')) {
// 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

View File

@@ -39,8 +39,8 @@ StarTranslator._translate = function (bundlePath) {
"builtBy": "Star translator",
"programs": [
{
"name": "client",
"arch": "browser",
"name": "web.browser",
"arch": "web.browser",
"path": "client.json"
},
{
@@ -99,7 +99,7 @@ StarTranslator._writeClientProg = function (bundlePath, clientProgPath) {
"app.json"),
'utf8'));
var clientManifest = {
"format": "browser-program-pre1",
"format": "web-program-pre1",
"manifest": origClientManifest.manifest,
// XXX Haven't updated this for the app.html -> head/body change, but
// surely we don't need to because code in pre-star apps doesn't

View File

@@ -6,7 +6,7 @@ var Future = Npm.require('fibers/future');
Plugin.registerSourceHandler("styl", function (compileStep) {
// XXX annoying that this is replicated in .css, .less, and .styl
if (! compileStep.archMatches('browser')) {
if (! compileStep.archMatches('web')) {
// 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

View File

@@ -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("web"))
// 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

View File

@@ -417,7 +417,7 @@ var runWebAppServer = function () {
__meteor_bootstrap__.configJson.client);
clientDir = path.dirname(clientJsonPath);
clientJson = JSON.parse(readUtf8FileSync(clientJsonPath));
if (clientJson.format !== "browser-program-pre1")
if (clientJson.format !== "web-program-pre1")
throw new Error("Unsupported format for client assets: " +
JSON.stringify(clientJson.format));

View File

@@ -51,19 +51,19 @@
// really the build tool can lay out the star however it wants.
//
//
// == Format of a program when arch is "browser.*" ==
// == Format of a program when arch is "web.*" ==
//
// Standard:
//
// /program.json
//
// - format: "browser-program-pre1" for this version
// - format: "web-program-pre1" for this version
//
// - manifest: array of resources to serve with HTTP, each an object:
// - path: path of file relative to program.json
// - where: "client"
// - type: "js", "css", or "asset"
// - cacheable: is it safe to ask the browser to cache this file (boolean)
// - cacheable: is it safe to ask the client to cache this file (boolean)
// - url: relative url to download the resource, includes cache busting
// parameter when used
// - size: size of file in bytes
@@ -387,7 +387,7 @@ var Target = function (options) {
// PackageLoader to use for resolving package dependenices.
self.packageLoader = options.packageLoader;
// Something like "browser.w3c" or "os" or "os.osx.x86_64"
// Something like "web.browser" or "os" or "os.osx.x86_64"
self.arch = options.arch;
// All of the Unibuilds that are to go into this target, in the order
@@ -411,7 +411,7 @@ var Target = function (options) {
self.nodeModulesDirectories = {};
// Static assets to include in the bundle. List of File.
// For browser targets, these are served over HTTP.
// For client targets, these are served over HTTP.
self.asset = [];
};
@@ -579,7 +579,7 @@ _.extend(Target.prototype, {
_emitResources: function () {
var self = this;
var isBrowser = archinfo.matches(self.arch, "browser");
var isWeb = archinfo.matches(self.arch, "web");
var isOs = archinfo.matches(self.arch, "os");
// Copy their resources into the bundle in order
@@ -607,7 +607,7 @@ _.extend(Target.prototype, {
: stripLeadingSlash(resource.servePath);
f.setTargetPathFromRelPath(relPath);
if (isBrowser)
if (isWeb)
f.setUrlFromRelPath(resource.servePath);
else {
unibuildAssets[resource.path] = resource.data;
@@ -622,7 +622,7 @@ _.extend(Target.prototype, {
return; // already handled
if (_.contains(["js", "css"], resource.type)) {
if (resource.type === "css" && ! isBrowser)
if (resource.type === "css" && ! isWeb)
// XXX might be nice to throw an error here, but then we'd
// have to make it so that package.js ignores css files
// that appear in the server directories in an app tree
@@ -636,7 +636,7 @@ _.extend(Target.prototype, {
var relPath = stripLeadingSlash(resource.servePath);
f.setTargetPathFromRelPath(relPath);
if (isBrowser) {
if (isWeb) {
f.setUrlFromRelPath(resource.servePath);
}
@@ -673,8 +673,8 @@ _.extend(Target.prototype, {
}
if (_.contains(["head", "body"], resource.type)) {
if (! isBrowser)
throw new Error("HTML segments can only go to the browser");
if (! isWeb)
throw new Error("HTML segments can only go to the client");
self[resource.type].push(resource.data);
return;
}
@@ -760,8 +760,8 @@ var ClientTarget = function (options) {
self.head = [];
self.body = [];
if (! archinfo.matches(self.arch, "browser"))
throw new Error("ClientTarget targeting something that isn't a browser?");
if (! archinfo.matches(self.arch, "web"))
throw new Error("ClientTarget targeting something that isn't a client?");
};
util.inherits(ClientTarget, Target);
@@ -953,7 +953,7 @@ _.extend(ClientTarget.prototype, {
// Control file
builder.writeJson('program.json', {
format: "browser-program-pre1",
format: "web-program-pre1",
manifest: manifest
});
return "program.json";
@@ -1296,7 +1296,7 @@ var JsImageTarget = function (options) {
Target.apply(this, arguments);
if (! archinfo.matches(self.arch, "os"))
// Conceivably we could support targeting the browser as long as
// Conceivably we could support targeting the client as long as
// no native node modules were used. No use case for that though.
throw new Error("JsImageTarget targeting something unusual?");
};
@@ -1615,7 +1615,9 @@ var writeSiteArchive = function (targets, outputPath, options) {
*
* - buildOptions: may include
* - minify: minify the CSS and JS assets (boolean, default false)
* - arch: the server architecture to target (defaults to archinfo.host())
* - serverArch: the server architecture to target
* (defaults to archinfo.host())
* - webArchs: an array of web archs to target
*
* - hasCachedBundle: true if we already have a cached bundle stored in
* /build. When true, we only build the new client targets in the bundle.
@@ -1648,7 +1650,8 @@ exports.bundle = function (options) {
if (! release.usingRightReleaseForApp(appDir))
throw new Error("running wrong release for app?");
var arch = buildOptions.arch || archinfo.host();
var serverArch = buildOptions.serverArch || archinfo.host();
var webArchs = buildOptions.webArchs || [ "web.browser" ];
var releaseName =
release.current.isCheckout() ? "none" : release.current.name;
@@ -1666,7 +1669,7 @@ exports.bundle = function (options) {
}, function () {
var packageLoader = project.project.getPackageLoader();
var downloaded = project.project._ensurePackagesExistOnDisk(
project.project.dependencies, { arch: arch, verbose: true });
project.project.dependencies, { serverArch: serverArch, verbose: true });
if (_.keys(downloaded).length !==
_.keys(project.project.dependencies).length) {
@@ -1677,10 +1680,10 @@ exports.bundle = function (options) {
var controlProgram = null;
var makeClientTarget = function (app) {
var makeClientTarget = function (app, webArch) {
var client = new ClientTarget({
packageLoader: packageLoader,
arch: "browser"
arch: webArch
});
client.make({
@@ -1695,7 +1698,7 @@ exports.bundle = function (options) {
var makeBlankClientTarget = function () {
var client = new ClientTarget({
packageLoader: packageLoader,
arch: "browser"
arch: "web.browser"
});
client.make({
minify: buildOptions.minify,
@@ -1708,7 +1711,7 @@ exports.bundle = function (options) {
var makeServerTarget = function (app, clientTarget) {
var targetOptions = {
packageLoader: packageLoader,
arch: arch,
arch: serverArch,
releaseName: releaseName
};
if (clientTarget)
@@ -1738,13 +1741,23 @@ exports.bundle = function (options) {
appDir, exports.ignoreFiles);
// Client
var client = makeClientTarget(app);
targets.client = client;
_.each(webArchs, function (arch) {
var client = makeClientTarget(app, arch);
targets[arch] = client;
});
// Create a browser client if one doesn't exist already.
var browserClient = targets["web.browser"];
if (! browserClient) {
browserClient = makeBlankClientTarget(app);
targets["web.browser"] = browserClient;
}
// Server
if (! options.hasCachedBundle) {
var server = makeServerTarget(app, client);
server.clientTarget = client;
var server = makeServerTarget(app, browserClient);
server.clientTarget = browserClient;
targets.server = server;
}
}

View File

@@ -186,12 +186,15 @@ main.registerCommand({
}
// Warn the user if their package is not good for all architectures.
if (compileResult.unipackage.buildArchitectures() !== "browser+os") {
var allArchs = compileResult.unipackage.buildArchitectures().split('+');
if (_.any(allArchs, function (arch) {
return arch.match(/^os\./);
})) {
process.stdout.write(
"\nWARNING: Your package contains binary code and is only compatible with " +
archinfo.host() + " architecture.\n" +
"Please use publish-for-arch to publish new builds of the package.\n\n");
};
}
// We are only publishing one package, so we should close the connection, and
// then exit with the previous error code.
@@ -763,15 +766,18 @@ main.registerCommand({
var myBuilds = _.pluck(
catalog.official.getAllBuilds(name, version),
'buildArchitectures');
// This package does not have different builds, so we don't care.
if (_.isEqual(myBuilds, ["browser+os"])) {
return versionRecord;
// Does this package only have a cross-platform build?
if (myBuilds.length === 1) {
var allArches = myBuilds[0].split('+');
if (!_.any(allArches, function (arch) {
return arch.match(/^os\./);
})) {
return versionRecord;
}
}
// This package is only available for some architectures.
var myStringBuilds = "";
_.each(myBuilds, function (build) {
myStringBuilds = myStringBuilds + build.split('+')[1] + " ";
});
// XXX show in a more human way?
var myStringBuilds = myBuilds.join(' ');
return _.extend({ buildArchitectures: myStringBuilds },
versionRecord);
};

View File

@@ -1308,7 +1308,7 @@ main.registerCommand({
}, function () {
tmpTropo.maybeDownloadPackageForArchitectures(
{packageName: toolPkg.package, version: toolPkg.constraint},
[osArch], // XXX 'browser' too?
[osArch], // XXX 'web.browser' too?
true);
});
_.each(release.packages, function (pkgVersion, pkgName) {
@@ -1317,7 +1317,7 @@ main.registerCommand({
}, function () {
tmpTropo.maybeDownloadPackageForArchitectures(
{packageName: pkgName, version: pkgVersion},
[osArch], // XXX 'browser' too?
[osArch], // XXX 'web.browser' too?
true);
});
});

View File

@@ -28,7 +28,7 @@ var compiler = exports;
// end up as watched dependencies. (At least for now, packages only used in
// target creation (eg minifiers and dev-bundle-fetcher) don't require you to
// update BUILT_BY, though you will need to quit and rerun "meteor run".)
compiler.BUILT_BY = 'meteor/11';
compiler.BUILT_BY = 'meteor/12';
// XXX where should this go? I'll make it a random utility function
// for now
@@ -457,11 +457,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 web 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-web 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.
@@ -473,11 +473,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"
// Web 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
// Web 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
@@ -498,10 +498,10 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader,
// a closure, so that its vars are shared with other files
// in the module.
// - addAsset({ path: "my/image.png", data: Buffer })
// Add a file to serve as-is over HTTP (browser targets) or
// Add a file to serve as-is over HTTP (web targets) or
// to include as-is in the bundle (os targets).
// This time `data` is a Buffer rather than a string. For
// browser targets, it will be served at the exact path you
// web targets, it will be served at the exact path you
// request (concatenated with rootOutputPath). For server
// targets, the file can be retrieved by passing path to
// Assets.getText or Assets.getBinary.
@@ -514,7 +514,7 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader,
// line, column, and func are all optional.
//
// XXX for now, these handlers must only generate portable code
// (code that isn't dependent on the arch, other than 'browser'
// (code that isn't dependent on the arch, other than 'web'
// vs 'os') -- they can look at the arch that is provided
// but they can't rely on the running on that particular arch
// (in the end, an arch-specific unibuild will be emitted only if
@@ -572,9 +572,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader,
return ret;
},
appendDocument: function (options) {
if (! archinfo.matches(inputSourceArch.arch, "browser"))
if (! archinfo.matches(inputSourceArch.arch, "web"))
throw new Error("Document sections can only be emitted to " +
"browser targets");
"web targets");
if (options.section !== "head" && options.section !== "body")
throw new Error("'section' must be 'head' or 'body'");
if (typeof options.data !== "string")
@@ -586,9 +586,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader,
});
},
addStylesheet: function (options) {
if (! archinfo.matches(inputSourceArch.arch, "browser"))
if (! archinfo.matches(inputSourceArch.arch, "web"))
throw new Error("Stylesheets can only be emitted to " +
"browser targets");
"web targets");
if (typeof options.data !== "string")
throw new Error("'data' option to addStylesheet must be a string");
sourceIsWatched = true;
@@ -605,8 +605,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, "web"))
throw new Error("'bare' option may only be used for web targets");
sourceIsWatched = true;
js.push({
source: options.data,

View File

@@ -92,6 +92,17 @@ var loadOrderSort = function (templateExtensions) {
};
};
// XXX We currently have a 1 to 1 mapping between 'where' and 'arch'.
// In the future, we may let people specify different 'where' and 'arch'.
var mapWhereToArch = function (where) {
if (where === 'server') {
return 'os';
} else {
// Transform client.* into web.*
return 'web.' + where.split('.').slice(1).join('.');
}
};
///////////////////////////////////////////////////////////////////////////////
// SourceArch
///////////////////////////////////////////////////////////////////////////////
@@ -200,7 +211,7 @@ var PackageSource = function () {
// Path that will be prepended to the URLs of all resources emitted
// by this package (assuming they don't end up getting
// concatenated). For non-browser targets, the only effect this will
// concatenated). For non-web targets, the only effect this will
// have is to change the actual on-disk paths of the files in the
// bundle, for those that care to open up the bundle and look (but
// it's still nice to get it right).
@@ -293,6 +304,10 @@ var PackageSource = function () {
// overall package versions file (if one exists). In the future, we can make
// this option transparent to the user in package.js.
self.noVersionFile = false;
// The list of where that we can target. Doesn't include 'client' because
// it is expanded into 'client.*'.
self.allWheres = ['server', 'client.browser', 'client.cordova'];
};
@@ -699,16 +714,35 @@ _.extend(PackageSource.prototype, {
}
// source files used
var sources = {client: [], server: []};
var sources = {};
// symbols exported
var exports = {client: [], server: []};
var exports = {};
// 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 = {};
var implies = {};
_.each(self.allWheres, function (where) {
sources[where] = [];
exports[where] = [];
uses[where] = [];
implies[where] = [];
});
// Iterates over the list of target archs and calls f(arch) for all archs
// that match an element of 'wheres'.
var forAllMatchingWheres = function (wheres, f) {
_.each(wheres, function (where) {
_.each(self.allWheres, function (matchWhere) {
if (archinfo.matches(matchWhere, where)) {
f(matchWhere);
}
});
});
};
// For this old-style, on_use/on_test/where-based package, figure
// out its dependencies by calling its on_xxx functions and seeing
@@ -717,14 +751,11 @@ _.extend(PackageSource.prototype, {
// 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
// and the server. Call the former the client version of the
// package, and the latter the server version. Then, when a
// package is used, include it in both the client and the server
// by default. This simple strategy doesn't capture even 10% of
// the complexity possible with on_use, on_test, and where, but
// and the server. When a package is used, include it in both the client
// and the server by default. This simple strategy doesn't capture even
// 10% of the complexity possible with on_use, on_test, and where, but
// probably is sufficient for virtually all packages that actually
// exist in the field, if not every single
// one. #OldStylePackageSupport
// exist in the field, if not every single one. #OldStylePackageSupport
if (fileAndDepLoader) {
var toArray = function (x) {
@@ -733,25 +764,23 @@ _.extend(PackageSource.prototype, {
return x ? [x] : [];
};
var allWheres = ['client', 'server'];
var toWhereArray = function (where) {
if (!(where instanceof Array)) {
where = where ? [where] : allWheres;
where = where ? [where] : self.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(self.allWheres, function (actualWhere) {
return archinfo.matches(actualWhere, inputWhere);
}));
if (! isMatch) {
buildmessage.error(
"Invalid 'where' argument: '" + badWheres[i] + "'",
"Invalid 'where' argument: '" + inputWhere + "'",
// skip toWhereArray in addition to the actual API function
{useMyCaller: 1});
};
// recover by using the real ones only
}
return realWhere;
{useMyCaller: 2});
}
});
return where;
};
var api = {
@@ -759,8 +788,9 @@ _.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.
// The default is ['client', 'server'].
// @param where 'web', 'web.browser', 'web.cordova', 'server',
// or an array of those.
// The default is ['web', 'server'].
//
// options can include:
//
@@ -808,7 +838,7 @@ _.extend(PackageSource.prototype, {
}
_.each(names, function (name) {
_.each(where, function (w) {
forAllMatchingWheres(where, function (w) {
uses[w].push(_.extend(utils.splitConstraint(name), {
unordered: options.unordered || false,
weak: options.weak || false
@@ -825,7 +855,7 @@ _.extend(PackageSource.prototype, {
where = toWhereArray(where);
_.each(names, function (name) {
_.each(where, function (w) {
forAllMatchingWheres(where, function (w) {
// We don't allow weak or unordered implies, since the main
// purpose of imply is to provide imports and plugins.
implies[w].push(utils.splitConstraint(name));
@@ -841,7 +871,7 @@ _.extend(PackageSource.prototype, {
where = toWhereArray(where);
_.each(paths, function (path) {
_.each(where, function (w) {
forAllMatchingWheres(where, function (w) {
var source = {relPath: path};
if (fileOptions)
source.fileOptions = fileOptions;
@@ -877,8 +907,9 @@ _.extend(PackageSource.prototype, {
// Export symbols from this package.
//
// @param symbols String (eg "Foo") or array of String
// @param where 'client', 'server', or an array of those.
// The default is ['client', 'server'].
// @param where 'web', 'server', 'web.browser', 'web.cordova'
// or an array of those.
// The default is ['web', 'server'].
// @param options 'testOnly', boolean.
export: function (symbols, where, options) {
// Support `api.export("FooTest", {testOnly: true})` without
@@ -900,7 +931,7 @@ _.extend(PackageSource.prototype, {
// recover by ignoring
return;
}
_.each(where, function (w) {
forAllMatchingWheres(where, function (w) {
exports[w].push({name: symbol, testOnly: !!options.testOnly});
});
});
@@ -918,7 +949,11 @@ _.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 = {};
_.each(self.allWheres, function (where) {
sources[where] = [];
});
fileAndDepLoader = null;
self.pluginInfo = {};
npmDependencies = null;
@@ -944,9 +979,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(self.allWheres, function (label) {
uses[label] = _.map(uses[label], setFromRel);
implies[label] = _.map(implies[label], setFromRel);
});
};
@@ -983,9 +1018,10 @@ _.extend(PackageSource.prototype, {
files.rm_recursive(path.join(self.sourceRoot, '.npm', f));
});
// Create source architectures, one for the server and one for the client.
_.each(["browser", "os"], function (arch) {
var where = (arch === "browser") ? "client" : "server";
// Create source architectures, one for the server and one for each web
// arch.
_.each(self.allWheres, function (where) {
var arch = mapWhereToArch(where);
// Everything depends on the package 'meteor', which sets up
// the basic environment) (except 'meteor' itself, and js-analyze
@@ -1065,15 +1101,17 @@ _.extend(PackageSource.prototype, {
self.sourceRoot = appDir;
self.serveRoot = path.sep;
_.each(["client", "server"], function (archName) {
_.each(self.allWheres, function (where) {
// Determine used packages
var project = require('./project.js').project;
var names = project.getConstraints();
var arch = archName === "server" ? "os" : "browser";
var arch = mapWhereToArch(where);
// XXX what about /client.browser/* etc, these directories could also
// be for specific client targets.
// Create unibuild
var sourceArch = new SourceArch(self, {
name: archName,
name: where,
arch: arch,
uses: _.map(names, utils.dealConstraint)
});
@@ -1118,7 +1156,7 @@ _.extend(PackageSource.prototype, {
});
var otherUnibuildRegExp =
(archName === "server" ? /^client\/$/ : /^server\/$/);
(where === "server" ? /^client\/$/ : /^server\/$/);
// The paths that we've called checkForInfiniteRecursion on.
var seenPaths = {};
@@ -1188,7 +1226,7 @@ _.extend(PackageSource.prototype, {
// Special case: on the client, JavaScript files in a
// `client/compatibility` directory don't get wrapped in a closure.
if (archName === "client" && relPath.match(/\.js$/)) {
if (archinfo.matches(arch, "web") && relPath.match(/\.js$/)) {
var clientCompatSubstr =
path.sep + 'client' + path.sep + 'compatibility' + path.sep;
if ((path.sep + relPath).indexOf(clientCompatSubstr) !== -1)
@@ -1198,7 +1236,7 @@ _.extend(PackageSource.prototype, {
});
// Now look for assets for this unibuild.
var assetDir = archName === "client" ? "public" : "private";
var assetDir = archinfo.matches(arch, "web") ? "public" : "private";
var assetDirs = readAndWatchDirectory('', {
include: [new RegExp('^' + assetDir + '/$')]
});

View File

@@ -649,7 +649,7 @@ _.extend(Project.prototype, {
var self = this;
buildmessage.assertInCapture();
options = options || {};
var arch = options.arch || archinfo.host();
var serverArch = options.serverArch || archinfo.host();
var verbose = options.verbose || !self.muted;
var downloadedPackages = {};
_.each(versions, function (version, name) {
@@ -657,7 +657,7 @@ _.extend(Project.prototype, {
try {
var available = tropohouse.default.maybeDownloadPackageForArchitectures(
packageVersionInfo,
['browser', arch],
[serverArch], // XXX 'web.browser' too?
verbose /* print downloading message */
);
downloadedPackages[name] = version;

View File

@@ -339,10 +339,15 @@ _.extend(OutputLog.prototype, {
// 'fake-mongod' stub process to be started instead of 'mongod'. The
// tellMongo method then becomes available on Runs for controlling
// the stub.
// - clients
// - browserstack: true if browserstack clients should be used
// - port: the port that the clients should run on
var Sandbox = function (options) {
var self = this;
options = options || {};
// default options
options = _.extend({ clients: {} }, options);
self.root = files.mkdtemp();
self.warehouse = null;
@@ -369,13 +374,13 @@ var Sandbox = function (options) {
self.clients = [ new PhantomClient({
host: 'localhost',
port: 3000
port: options.clients.port || 3000
})];
if (options.clients && options.clients.browserstack) {
self.clients.push(new BrowserStackClient({
host: 'localhost',
port: 3000
port: options.clients.port || 3000
}));
}
@@ -410,6 +415,7 @@ _.extend(Sandbox.prototype, {
// });
testWithAllClients: function (f) {
var self = this;
var argsArray = _.compact(_.toArray(arguments).slice(1));
if (self.clients.length) {
console.log("running test with " + self.clients.length + " client(s).");
@@ -421,7 +427,7 @@ _.extend(Sandbox.prototype, {
console.log("testing with " + client.name + "...");
f(new Run(self.execPath, {
sandbox: self,
args: [],
args: argsArray,
cwd: self.cwd,
env: self._makeEnv(),
fakeMongo: self.fakeMongo,
@@ -528,7 +534,7 @@ _.extend(Sandbox.prototype, {
};
self.write(to, contents);
},
// Delete a file in the sandbox. 'filename' is as in write().
unlink: function (filename) {
var self = this;
@@ -725,7 +731,7 @@ _.extend(Sandbox.prototype, {
// Insert into builds. Assume the package is available for all
// architectures.
stubCatalog.collections.builds.push({
buildArchitectures: "browser+os",
buildArchitectures: "web.browser+os",
versionId: versionRec._id,
build: buildRec.build,
_id: utils.randomToken()
@@ -1566,5 +1572,6 @@ _.extend(exports, {
fail: fail,
expectEqual: expectEqual,
expectThrows: expectThrows,
getToolsPackage: getToolsPackage
getToolsPackage: getToolsPackage,
execFileSync: execFileSync
});

View File

@@ -43,6 +43,7 @@ var logsOrMongoForApp = function (sandbox, command, appName, options) {
run.waitSecs(commandTimeoutSecs);
var expectSuccess = selftest.markStack(function () {
run.waitSecs(2);
run.match(matchString);
run.expectExit(0);
});

View File

@@ -48,7 +48,7 @@ var runTest = function () {
});
var clientManifest = JSON.parse(
fs.readFileSync(
path.join(tmpOutputDir, "programs", "client", "program.json")
path.join(tmpOutputDir, "programs", "web.browser", "program.json")
)
);
@@ -59,7 +59,7 @@ var runTest = function () {
return m.url === file[0];
});
assert(manifestItem);
var diskPath = path.join(tmpOutputDir, "programs", "client",
var diskPath = path.join(tmpOutputDir, "programs", "web.browser",
manifestItem.path);
assert(fs.existsSync(diskPath));
assert.strictEqual(fs.readFileSync(diskPath, "utf8"), file[1]);

View File

@@ -37,7 +37,7 @@ var setAppDir = function (appDir) {
var runTest = function () {
var readManifest = function (tmpOutputDir) {
return JSON.parse(fs.readFileSync(
path.join(tmpOutputDir, "programs", "client", "program.json"),
path.join(tmpOutputDir, "programs", "web.browser", "program.json"),
"utf8")).manifest;
};

View File

@@ -116,7 +116,7 @@ _.extend(exports.Tropohouse.prototype, {
throw e;
}
if (packageLinkTarget) {
// The symlink will be of the form '.VERSION.RANDOMTOKEN++browser+os',
// The symlink will be of the form '.VERSION.RANDOMTOKEN++web.browser+os',
// so this strips off the part before the '++'.
// XXX maybe we should just read the unipackage.json instead of
// depending on the symlink?

View File

@@ -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, "web"),
name: self.pkg.name || null
});
@@ -307,7 +307,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
// web+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
@@ -338,7 +338,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 'web'), or throw an exception if that
// packages can't be loaded under these circumstances.
getUnibuildAtArch: function (arch) {
var self = this;