Allow meteor.mainModule.{client,server} === false for no entry point.

If meteor.mainModule.{client,server,...} === false, no modules will be
loaded eagerly for that architecture. This is useful if you have an app
with no special app/{client,server} directory structure and you want to
specify an entry point for just the client (or just the server), without
accidentally loading everything on the other architecture.
This commit is contained in:
Ben Newman
2018-02-27 10:48:26 -05:00
parent 66508c483a
commit 8dd2402f76
4 changed files with 82 additions and 20 deletions

View File

@@ -1014,6 +1014,12 @@ _.extend(PackageSource.prototype, {
}
if (typeof mainModule !== "undefined") {
// Note: if mainModule === false, no JavaScript modules will be
// loaded eagerly unless explicitly added with !fileOptions.lazy by
// a compiler plugin. This can be useful for building an app that
// does not run any application JS on the client (or the server). Of
// course, Meteor packages may still run JS on startup, but they
// have their own rules for lazy/eager loading of modules.
if (relPath === mainModule) {
fileOptions.lazy = false;
fileOptions.mainModule = true;

View File

@@ -1643,19 +1643,19 @@ export class MeteorConfig {
const configMainModule = this.get("mainModule");
const mainModulesByArch = Object.create(null);
if (configMainModule) {
if (typeof configMainModule === "string") {
// If packageJson.meteor.mainModule is a string, use that string
// as the mainModule for all architectures.
mainModulesByArch["os"] = configMainModule;
mainModulesByArch["web"] = configMainModule;
} else if (typeof configMainModule === "object") {
// If packageJson.meteor.mainModule is an object, use its
// properties to select a mainModule for each architecture.
Object.keys(configMainModule).forEach(where => {
mainModulesByArch[mapWhereToArch(where)] = configMainModule[where];
});
}
if (typeof configMainModule === "string" ||
configMainModule === false) {
// If packageJson.meteor.mainModule is a string or false, use that
// value as the mainModule for all architectures.
mainModulesByArch["os"] = configMainModule;
mainModulesByArch["web"] = configMainModule;
} else if (configMainModule &&
typeof configMainModule === "object") {
// If packageJson.meteor.mainModule is an object, use its properties
// to select a mainModule for each architecture.
Object.keys(configMainModule).forEach(where => {
mainModulesByArch[mapWhereToArch(where)] = configMainModule[where];
});
}
return mainModulesByArch;
@@ -1673,6 +1673,19 @@ export class MeteorConfig {
arch, Object.keys(mainModulesByArch));
if (mainMatch) {
const mainModule = mainModulesByArch[mainMatch];
if (mainModule === false) {
// If meteor.mainModule.{client,server,...} === false, no modules
// will be loaded eagerly on the {client,server...}. This is useful
// if you have an app with no special app/{client,server} directory
// structure and you want to specify an entry point for just the
// client (or just the server), without accidentally loading
// everything on the other architecture. Instead of omitting
// meteor.mainModule for the other architecture, set it to false.
return mainModule;
}
if (! this._resolversByArch[arch]) {
this._resolversByArch[arch] = new Resolver({
sourceRoot: this.appDirectory,
@@ -1685,7 +1698,7 @@ export class MeteorConfig {
// containing package.json or index.js files.
const res = this._resolversByArch[arch].resolve(
// Only relative paths are allowed (not top-level packages).
"./" + files.pathNormalize(mainModulesByArch[mainMatch]),
"./" + files.pathNormalize(mainModule),
this.packageJsonPath
);

View File

@@ -46,6 +46,26 @@ selftest.define("mainModule", function () {
check({});
check(false);
check({
client: false,
server: "abc",
});
check({
client: "abc",
server: false,
});
check({
web: false,
});
check({
os: false,
});
check({
client: "a",
os: "bc",

View File

@@ -9,6 +9,8 @@ const startupPromise = new Promise(resolve => {
Meteor.startup(resolve);
});
const hasOwn = Object.prototype.hasOwnProperty;
describe("meteor.mainModule", () => {
// These tests test the consequences of having various meteor.mainModule
// configurations in package.json.
@@ -50,7 +52,13 @@ describe("meteor.mainModule", () => {
it("loads the right files", async () => {
await startupPromise;
function checkNoMainModule() {
if (Meteor.isClient) {
console.log("client config:", config);
} else {
console.log("server config:", config);
}
function checkDefaultLoadRules() {
assert.deepEqual(ids, [
"/a.js",
"/b.js",
@@ -61,9 +69,22 @@ describe("meteor.mainModule", () => {
]);
}
function checkEagerLoadingDisabled() {
// Eager loading of all modules is disabled.
assert.deepEqual(ids, []);
}
if (! config ||
! config.mainModule) {
return checkNoMainModule();
! hasOwn.call(config, "mainModule")) {
return checkDefaultLoadRules();
}
if (config.mainModule === false) {
return checkEagerLoadingDisabled();
}
if (! config.mainModule) {
return checkDefaultLoadRules();
}
let mainId;
@@ -72,16 +93,18 @@ describe("meteor.mainModule", () => {
mainId =
config.mainModule.client ||
config.mainModule.web;
console.log("client config:", config);
} else if (Meteor.isServer) {
mainId =
config.mainModule.server ||
config.mainModule.os;
console.log("server config:", config);
}
if (mainId === false) {
return checkEagerLoadingDisabled();
}
if (! mainId) {
return checkNoMainModule();
return checkDefaultLoadRules();
}
const absId = require.resolve("./" + mainId);