mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
First attempt at runtime for loading packages with TLA
This commit is contained in:
@@ -1,9 +1,28 @@
|
||||
function PackageRegistry() {
|
||||
this._promiseInfoMap = Object.create(null);
|
||||
this._packageQueue = [];
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
var PRp = PackageRegistry.prototype;
|
||||
|
||||
var ASYNC_MAIN_MODULE = {};
|
||||
|
||||
// If potentialPromise is a promise, calls callback with the resolved value
|
||||
// Otherwise, synchronously calls the callback with the value
|
||||
PRp._waitForModule = function (potentialPromise, callback) {
|
||||
if (
|
||||
isThenable(potentialPromise) &&
|
||||
potentialPromise.asyncMainModule === ASYNC_MAIN_MODULE
|
||||
) {
|
||||
potentialPromise.then((results) => {
|
||||
callback(results);
|
||||
});
|
||||
} else {
|
||||
callback(potentialPromise);
|
||||
}
|
||||
}
|
||||
|
||||
// Set global.Package[name] = pkg || {}. If additional arguments are
|
||||
// supplied, their keys will be copied into pkg if not already present.
|
||||
// This method is defined on the prototype of global.Package so that it
|
||||
@@ -28,6 +47,12 @@ PRp._define = function definePackage(name, pkg) {
|
||||
info.resolve(pkg);
|
||||
}
|
||||
|
||||
// if (this._packageQueue.length > 0) {
|
||||
// this._packageQueue.unshift().load();
|
||||
// } else {
|
||||
// this._running = false;
|
||||
// }
|
||||
|
||||
return pkg;
|
||||
};
|
||||
|
||||
@@ -60,6 +85,95 @@ PRp._promise = function promise(name) {
|
||||
return info.promise;
|
||||
};
|
||||
|
||||
// On the server, load is run immediately
|
||||
// If it has async modules that are eagerly evaluated, it will return a
|
||||
// promise that resolves after the package has been fully loaded.
|
||||
PRp.load = function (name, deps, load) {
|
||||
// console.log('start load', name);
|
||||
var self = this;
|
||||
|
||||
if (typeof Meteor === 'undefined' || Meteor.isServer) {
|
||||
var result = load() || {};
|
||||
|
||||
var mainModule = result.mainModule;
|
||||
var exports = result.exports;
|
||||
|
||||
if (mainModule && mainModule.asyncMainModule === ASYNC_MAIN_MODULE) {
|
||||
return mainModule.then(function (mainExports) {
|
||||
console.log('defineAsync', name, mainModule, exports);
|
||||
self._define(name, mainExports, exports);
|
||||
});
|
||||
}
|
||||
|
||||
self._define(name, mainModule, exports);
|
||||
// console.log('define', name, mainModule, exports);
|
||||
return;
|
||||
// this._packageQueue.push({
|
||||
// name: name,
|
||||
// load: load,
|
||||
// deps: deps
|
||||
// });
|
||||
|
||||
// if (!this._running) {
|
||||
// this._running = true;
|
||||
// this._packageQueue.unshift().load();
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
|
||||
}
|
||||
|
||||
function isThenable(value) {
|
||||
return typeof value === 'object' && value !== null &&
|
||||
typeof value.then === 'function';
|
||||
}
|
||||
|
||||
PRp._evaluateEagerModules = function(require, paths, mainModuleIndex) {
|
||||
let index = -1;
|
||||
let result;
|
||||
let promise;
|
||||
let resolve;
|
||||
|
||||
function evaluateNext() {
|
||||
index += 1;
|
||||
|
||||
if (index === paths.length) {
|
||||
if (resolve) {
|
||||
resolve(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let path = paths[index];
|
||||
let exports = require(path);
|
||||
|
||||
if (isThenable(exports)) {
|
||||
if (!promise) {
|
||||
promise = new Promise(_resolve => resolve = _resolve);
|
||||
promise.asyncMainModule = ASYNC_MAIN_MODULE;
|
||||
}
|
||||
|
||||
exports.then(resolvedExports => {
|
||||
if (index === mainModuleIndex) {
|
||||
result = resolvedExports;
|
||||
}
|
||||
evaluateNext();
|
||||
});
|
||||
// TODO: handle error
|
||||
} else {
|
||||
if (index === mainModuleIndex) {
|
||||
result = exports;
|
||||
}
|
||||
evaluateNext();
|
||||
}
|
||||
}
|
||||
|
||||
evaluateNext();
|
||||
|
||||
return promise ? promise : result;
|
||||
}
|
||||
|
||||
// Initialize the Package namespace used by all Meteor packages.
|
||||
global.Package = new PackageRegistry();
|
||||
|
||||
|
||||
@@ -439,7 +439,7 @@ Object.assign(Module.prototype, {
|
||||
assert.ok(_.isNumber(moduleCount));
|
||||
assert.ok(_.isNumber(sourceWidth));
|
||||
|
||||
let exportsName;
|
||||
let exportsIndex = -1;
|
||||
|
||||
// Now that we have installed everything in this package or
|
||||
// application, first evaluate the bare files, then require the
|
||||
@@ -458,21 +458,33 @@ Object.assign(Module.prototype, {
|
||||
});
|
||||
|
||||
if (eagerModuleFiles.length > 0) {
|
||||
_.each(eagerModuleFiles, file => {
|
||||
let code = 'Package._evaluateEagerModules(require,[';
|
||||
let paths = eagerModuleFiles.map((file, index) => {
|
||||
if (file.mainModule) {
|
||||
exportsName = "exports";
|
||||
exportsIndex = index;
|
||||
}
|
||||
|
||||
chunks.push(
|
||||
file.mainModule ? "\nvar " + exportsName + " = " : "\n",
|
||||
"require(",
|
||||
JSON.stringify(file.absModuleId),
|
||||
");"
|
||||
);
|
||||
return JSON.stringify(file.absModuleId);
|
||||
});
|
||||
|
||||
code += paths.join(',\n ');
|
||||
code += ']';
|
||||
|
||||
if (exportsIndex !== -1) {
|
||||
code += `, ${exportsIndex}`;
|
||||
}
|
||||
|
||||
code += ');';
|
||||
|
||||
if (exportsIndex !== -1) {
|
||||
code = '\nvar exports = ' + code;
|
||||
}
|
||||
|
||||
chunks.push(code);
|
||||
}
|
||||
|
||||
return exportsName;
|
||||
|
||||
return exportsIndex === -1 ? undefined : 'exports';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -964,8 +976,22 @@ var SOURCE_MAP_INSTRUCTIONS_COMMENT = banner([
|
||||
var getHeader = function (options) {
|
||||
var chunks = [];
|
||||
|
||||
// TODO: find a better check that also works for packages that
|
||||
// load before the Meteor package
|
||||
if (options.name !== 'meteor') {
|
||||
let depsCode = Object.values(options.imports).map(k => JSON.stringify(k)).join(', ');
|
||||
|
||||
chunks.push(
|
||||
`Package.load("${options.name}", [`,
|
||||
depsCode,
|
||||
'], function () {'
|
||||
);
|
||||
|
||||
} else {
|
||||
chunks.push("(function() {\n\n")
|
||||
}
|
||||
|
||||
chunks.push(
|
||||
"(function () {\n\n",
|
||||
getImportCode(options.imports, "/* Imports */\n", false),
|
||||
);
|
||||
|
||||
@@ -1015,33 +1041,47 @@ var getFooter = function ({
|
||||
name,
|
||||
exported,
|
||||
exportsName,
|
||||
imports
|
||||
}) {
|
||||
var chunks = [];
|
||||
|
||||
if (name && exported) {
|
||||
if (name === 'meteor') {
|
||||
chunks.push("Package._define(" + JSON.stringify(name) + ", ");
|
||||
if (!_.isEmpty(exported)) {
|
||||
const scratch = {};
|
||||
_.each(exported, symbol => scratch[symbol] = symbol);
|
||||
const symbolTree = writeSymbolTree(buildSymbolTree(scratch));
|
||||
chunks.push(symbolTree);
|
||||
}
|
||||
chunks.push(');\n');
|
||||
|
||||
} else if (exported || exportsName) {
|
||||
chunks.push("\n\n/* Exports */\n");
|
||||
chunks.push('return {\n');
|
||||
|
||||
if (exportsName) {
|
||||
chunks.push(` mainModule: ${exportsName},`);
|
||||
}
|
||||
|
||||
// Even if there are no exports, we need to define Package.foo,
|
||||
// because the existence of Package.foo is how another package
|
||||
// (e.g., one that weakly depends on foo) can tell if foo is loaded.
|
||||
chunks.push("Package._define(" + JSON.stringify(name));
|
||||
|
||||
if (exportsName) {
|
||||
// If we have an exports object, use it as Package[name].
|
||||
chunks.push(", ", exportsName);
|
||||
}
|
||||
|
||||
if (! _.isEmpty(exported)) {
|
||||
const scratch = {};
|
||||
_.each(exported, symbol => scratch[symbol] = symbol);
|
||||
const symbolTree = writeSymbolTree(buildSymbolTree(scratch));
|
||||
chunks.push(", ", symbolTree);
|
||||
chunks.push("exports: ", symbolTree);
|
||||
}
|
||||
|
||||
chunks.push(");\n");
|
||||
chunks.push('};\n');
|
||||
|
||||
}
|
||||
if (name !== 'meteor') {
|
||||
chunks.push("\n});\n");
|
||||
} else {
|
||||
chunks.push("\n})();\n");
|
||||
}
|
||||
|
||||
chunks.push("\n})();\n");
|
||||
return chunks.join('');
|
||||
};
|
||||
|
||||
@@ -1150,6 +1190,7 @@ export var fullLink = Profile("linker.fullLink", function (inputFiles, {
|
||||
// Otherwise we're making a package and we have to actually combine the files
|
||||
// into a single scope.
|
||||
var header = getHeader({
|
||||
name,
|
||||
imports,
|
||||
packageVariables: _.union(assignedVariables, declaredExports)
|
||||
});
|
||||
@@ -1164,7 +1205,8 @@ export var fullLink = Profile("linker.fullLink", function (inputFiles, {
|
||||
var footer = getFooter({
|
||||
exported: declaredExports,
|
||||
exportsName,
|
||||
name
|
||||
name,
|
||||
imports
|
||||
});
|
||||
|
||||
if (includeSourceMapInstructions) {
|
||||
|
||||
Reference in New Issue
Block a user