Files
meteor/packages/core-runtime/load-js-image.js
2023-01-23 17:10:43 -04:00

155 lines
4.4 KiB
JavaScript

// This file needs to work in old web browsers and old node versions
// It should not use js features newer than EcmaScript 5.
//
// Handles loading linked code for packages and apps
// Ensures packages and eager requires run in the correct order
// when there is code that uses top level await
var pending = Object.create(null);
var hasOwn = Object.prototype.hasOwnProperty;
function queue(name, deps, runImage) {
pending[name] = [];
var pendingDepsCount = 0;
function onDepLoaded() {
pendingDepsCount -= 1;
if (pendingDepsCount === 0) {
load(name, runImage);
}
}
deps.forEach(function (dep) {
if (hasOwn.call(pending, dep)) {
pendingDepsCount += 1;
pending[dep].push(onDepLoaded);
} else {
// load must always be called for a package's dependencies first.
// If the package is not pending, then it must have already loaded
// or is a weak dependency, and the dependency is not being used.
}
});
if (pendingDepsCount === 0) {
load(name, runImage);
}
}
function load(name, runImage) {
var config = runImage();
runEagerModules(config, function (mainModuleExports) {
// Get the exports after the eager code has been run
var exports = config.export ? config.export() : {};
if (config.mainModulePath) {
Package._define(name, mainModuleExports, exports);
} else {
Package._define(name, exports);
}
var pendingCallbacks = pending[name];
delete pending[name];
pendingCallbacks.forEach(function (callback) {
callback();
});
});
}
function runEagerModules(config, callback) {
if (!config.eagerModulePaths) {
return callback();
}
var index = -1;
var mainExports = {};
var mainModuleAsync = false;
function evaluateNextModule() {
index += 1;
if (index === config.eagerModulePaths.length) {
if (mainModuleAsync) {
// Now that the package has loaded, mark the main module as sync
// This allows other packages and the app to `require` the package
// and for it to work the same, regardless of if it uses TLA or not
// XXX: this is a temporary hack until we find a better way to do this
const reify = config.require('/node_modules/meteor/modules/node_modules/@meteorjs/reify/lib/runtime');
reify._requireAsSync(config.mainModulePath);
}
return callback(mainExports);
}
var path = config.eagerModulePaths[index];
var exports = config.require(path);
// TODO[fibers]: retest the function checkAsyncModule. It looks like it's returning the wrong values
// returning false when exports is a promise
if (exports && exports.then) {
if (path === config.mainModulePath) {
mainModuleAsync = true;
}
// Is an async module
exports.then(function (exports) {
if (path === config.mainModulePath) {
mainExports = exports;
}
evaluateNextModule();
});
} else {
if (path === config.mainModulePath) {
mainExports = exports;
}
evaluateNextModule();
}
}
evaluateNextModule();
}
function checkAsyncModule (exports) {
// Uses descriptor to avoid running any getters
var isPromise = exports && hasOwn.call(exports, 'then') &&
typeof Object.getOwnPropertyDescriptor(exports, 'then').value === 'function';
if (!isPromise) {
return false;
}
return hasOwn.call(exports, '__reifyAsyncModule') &&
Object.getOwnPropertyDescriptor(exports, '__reifyAsyncModule');
}
// For this to be accurate, all linked files must be queued before calling this
// If all are loaded, returns null. Otherwise, returns a promise
function waitUntilAllLoaded() {
var pendingNames = Object.keys(pending);
if (pendingNames.length === 0) {
// All packages are loaded
// If there were no async packages, then there might not be a promise
// polyfill loaded either, so we don't create a promise to return
return null;
}
return new Promise(function (resolve) {
var pendingCount = pendingNames.length;
pendingNames.forEach(function (name) {
pending[name].push(function () {
pendingCount -= 1;
if (pendingCount === 0) {
resolve();
}
});
});
})
}
// Since the package.js doesn't export load or waitUntilReady
// these will never be globals in packages or apps that depend on core-runtime
Package['core-runtime'] = {
queue: queue,
waitUntilAllLoaded: waitUntilAllLoaded
};