Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

radekmie
Copy link
Collaborator

@radekmie radekmie commented Apr 15, 2024

Updated description

According to the @zodern's comments, I created a synchronous queue instead. It matches the expected loading order now (the tests are updated).

Previous description

This is a different attempt at solving https://forums.meteor.com/t/order-of-loading-packages/61112 (see #13074 for the first one). The idea is to keep the dependency loading order as it was, but maintain a queue to load at most one package in order.

How I tested it:

  • Use https://github.com/plbkbx/testLoadingPackages.
  • Add the following logging:
    diff --git a/packages/core-runtime/load-js-image.js b/packages/core-runtime/load-js-image.js
    index d18832c9fb..d1b9801b4c 100644
    --- a/packages/core-runtime/load-js-image.js
    +++ b/packages/core-runtime/load-js-image.js
    @@ -9,6 +9,7 @@ var pending = Object.create(null);
     var hasOwn = Object.prototype.hasOwnProperty;
     
     function queue(name, deps, runImage) {
    +  console.log(`queue(${name}, [${deps.join()}])`);
       pending[name] = [];
     
       var pendingDepsCount = 0;
    @@ -38,6 +39,7 @@ function queue(name, deps, runImage) {
     }
     
     function load(name, runImage) {
    +  console.log(`load(${name})`);
       var config = runImage();
     
       runEagerModules(config, function (mainModuleExports) {
Logs before
queue(meteor, [core-runtime])
load(meteor)
queue(meteor-base, [meteor])
load(meteor-base)
queue(npm-mongo, [meteor])
load(npm-mongo)
queue(modules-runtime, [meteor])
load(modules-runtime)
queue(modules, [meteor,modules-runtime])
load(modules)
queue(react-fast-refresh, [meteor,modules])
load(react-fast-refresh)
queue(ecmascript, [meteor,react-fast-refresh])
load(ecmascript)
queue(ecmascript-runtime, [meteor])
load(ecmascript-runtime)
queue(babel-runtime, [meteor,modules])
load(babel-runtime)
queue(modern-browsers, [meteor,modules])
load(modern-browsers)
queue(promise, [meteor,modules,modern-browsers])
load(promise)
queue(fetch, [meteor,modules,modern-browsers,promise])
load(fetch)
queue(inter-process-messaging, [meteor,modules,promise])
load(inter-process-messaging)
queue(dynamic-import, [meteor,modules,promise,fetch,modern-browsers,inter-process-messaging])
load(dynamic-import)
queue(es5-shim, [meteor,modules])
load(es5-shim)
queue(ecmascript-runtime-client, [meteor,modules,promise,modern-browsers])
load(ecmascript-runtime-client)
queue(ecmascript-runtime-server, [meteor,modules])
load(ecmascript-runtime-server)
queue(base64, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(base64)
queue(ejson, [meteor,ecmascript,base64,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(ejson)
queue(diff-sequence, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(diff-sequence)
queue(geojson-utils, [meteor,modules])
load(geojson-utils)
queue(id-map, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(id-map)
queue(random, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(random)
queue(mongo-id, [meteor,ejson,random,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(mongo-id)
queue(ordered-dict, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(ordered-dict)
queue(tracker, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(tracker)
queue(mongo-decimal, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(mongo-decimal)
queue(minimongo, [meteor,diff-sequence,ecmascript,ejson,geojson-utils,id-map,mongo-id,ordered-dict,random,tracker,mongo-decimal,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(minimongo)
queue(check, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(check)
queue(retry, [meteor,ecmascript,random,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(retry)
queue(callback-hook, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(callback-hook)
queue(ddp-common, [meteor,check,random,ecmascript,ejson,tracker,retry,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(ddp-common)
queue(reload, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(reload)
queue(socket-stream-client, [meteor,ecmascript,modern-browsers,retry,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(socket-stream-client)
queue(ddp-client, [meteor,check,random,ejson,tracker,retry,id-map,ecmascript,callback-hook,ddp-common,reload,socket-stream-client,diff-sequence,mongo-id,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(ddp-client)
queue(underscore, [meteor])
load(underscore)
queue(babel-compiler, [meteor,ecmascript-runtime,modern-browsers,ecmascript-runtime-client,ecmascript-runtime-server])
load(babel-compiler)
queue(typescript, [meteor,babel-compiler,react-fast-refresh])
load(typescript)
queue(logging, [meteor,ejson,ecmascript,typescript,ecmascript-runtime-client,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-server])
load(logging)
queue(routepolicy, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(routepolicy)
queue(boilerplate-generator, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(boilerplate-generator)
queue(webapp-hashing, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(webapp-hashing)
queue(webapp, [meteor,ecmascript,logging,underscore,routepolicy,modern-browsers,boilerplate-generator,webapp-hashing,inter-process-messaging,callback-hook,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(webapp)
queue(ddp-server, [meteor,check,random,ejson,underscore,retry,mongo-id,diff-sequence,ecmascript,ddp-common,ddp-client,webapp,routepolicy,callback-hook,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ddp, [meteor,ddp-client,ddp-server])
queue(allow-deny, [meteor,ecmascript,minimongo,check,ejson,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo-dev-server, [meteor,modules])
load(mongo-dev-server)
queue(binary-heap, [meteor,id-map,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
load(binary-heap)
queue(facts-base, [meteor,ecmascript,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo, [meteor,npm-mongo,allow-deny,random,ejson,minimongo,ddp,tracker,diff-sequence,mongo-id,check,ecmascript,logging,mongo-decimal,underscore,binary-heap,callback-hook,ddp-client,ddp-server,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(one, [meteor,mongo,ddp-server,ddp-client])
queue(two, [meteor,mongo])
queue(three, [meteor,mongo])
queue(hot-code-push, [meteor])
load(hot-code-push)
queue(autoupdate, [meteor,webapp,check,inter-process-messaging,ecmascript,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(null, [meteor-base,one,two,three,underscore,mongo,ecmascript,meteor,webapp,ddp,es5-shim,hot-code-push,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,autoupdate,reload,ecmascript-runtime-client,ecmascript-runtime-server])
load(ddp-server)
load(ddp)
load(allow-deny)
load(facts-base)
load(mongo)
load(two)
two.js
load(three)
three.js
load(one)
one.js
load(autoupdate)
load(null)
main.js
Logs after
queue(meteor, [core-runtime])
load(meteor)
queue(meteor-base, [meteor])
load(meteor-base)
queue(npm-mongo, [meteor])
load(npm-mongo)
queue(modules-runtime, [meteor])
load(modules-runtime)
queue(modules, [meteor,modules-runtime])
load(modules)
queue(react-fast-refresh, [meteor,modules])
queue(ecmascript, [meteor,react-fast-refresh])
queue(ecmascript-runtime, [meteor])
load(ecmascript-runtime)
queue(babel-runtime, [meteor,modules])
queue(modern-browsers, [meteor,modules])
queue(promise, [meteor,modules,modern-browsers])
queue(fetch, [meteor,modules,modern-browsers,promise])
queue(inter-process-messaging, [meteor,modules,promise])
queue(dynamic-import, [meteor,modules,promise,fetch,modern-browsers,inter-process-messaging])
queue(es5-shim, [meteor,modules])
queue(ecmascript-runtime-client, [meteor,modules,promise,modern-browsers])
queue(ecmascript-runtime-server, [meteor,modules])
queue(base64, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ejson, [meteor,ecmascript,base64,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(diff-sequence, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(geojson-utils, [meteor,modules])
queue(id-map, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(random, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo-id, [meteor,ejson,random,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ordered-dict, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(tracker, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo-decimal, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(minimongo, [meteor,diff-sequence,ecmascript,ejson,geojson-utils,id-map,mongo-id,ordered-dict,random,tracker,mongo-decimal,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(check, [meteor,ecmascript,ejson,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(retry, [meteor,ecmascript,random,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(callback-hook, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ddp-common, [meteor,check,random,ecmascript,ejson,tracker,retry,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(reload, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(socket-stream-client, [meteor,ecmascript,modern-browsers,retry,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ddp-client, [meteor,check,random,ejson,tracker,retry,id-map,ecmascript,callback-hook,ddp-common,reload,socket-stream-client,diff-sequence,mongo-id,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(underscore, [meteor])
load(underscore)
queue(babel-compiler, [meteor,ecmascript-runtime,modern-browsers,ecmascript-runtime-client,ecmascript-runtime-server])
queue(typescript, [meteor,babel-compiler,react-fast-refresh])
queue(logging, [meteor,ejson,ecmascript,typescript,ecmascript-runtime-client,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-server])
queue(routepolicy, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(boilerplate-generator, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(webapp-hashing, [meteor,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(webapp, [meteor,ecmascript,logging,underscore,routepolicy,modern-browsers,boilerplate-generator,webapp-hashing,inter-process-messaging,callback-hook,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ddp-server, [meteor,check,random,ejson,underscore,retry,mongo-id,diff-sequence,ecmascript,ddp-common,ddp-client,webapp,routepolicy,callback-hook,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(ddp, [meteor,ddp-client,ddp-server])
queue(allow-deny, [meteor,ecmascript,minimongo,check,ejson,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo-dev-server, [meteor,modules])
queue(binary-heap, [meteor,id-map,ecmascript,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(facts-base, [meteor,ecmascript,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(mongo, [meteor,npm-mongo,allow-deny,random,ejson,minimongo,ddp,tracker,diff-sequence,mongo-id,check,ecmascript,logging,mongo-decimal,underscore,binary-heap,callback-hook,ddp-client,ddp-server,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ecmascript-runtime-client,ecmascript-runtime-server])
queue(one, [meteor,mongo,ddp-server,ddp-client])
queue(two, [meteor,mongo])
queue(three, [meteor,mongo])
queue(hot-code-push, [meteor])
load(hot-code-push)
queue(autoupdate, [meteor,webapp,check,inter-process-messaging,ecmascript,ddp,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,ecmascript-runtime-client,ecmascript-runtime-server])
queue(null, [meteor-base,one,two,three,underscore,mongo,ecmascript,meteor,webapp,ddp,es5-shim,hot-code-push,modules,ecmascript-runtime,babel-runtime,promise,dynamic-import,ddp-client,ddp-server,autoupdate,reload,ecmascript-runtime-client,ecmascript-runtime-server])
load(react-fast-refresh)
load(babel-runtime)
load(modern-browsers)
load(es5-shim)
load(ecmascript-runtime-server)
load(geojson-utils)
load(mongo-dev-server)
load(ecmascript)
load(promise)
load(fetch)
load(inter-process-messaging)
load(ecmascript-runtime-client)
load(dynamic-import)
load(babel-compiler)
load(typescript)
load(base64)
load(random)
load(ordered-dict)
load(tracker)
load(callback-hook)
load(reload)
load(routepolicy)
load(boilerplate-generator)
load(webapp-hashing)
load(ejson)
load(retry)
load(diff-sequence)
load(id-map)
load(mongo-id)
load(mongo-decimal)
load(check)
load(logging)
load(socket-stream-client)
load(binary-heap)
load(minimongo)
load(ddp-common)
load(webapp)
load(ddp-client)
load(ddp-server)
load(ddp)
load(allow-deny)
load(facts-base)
load(autoupdate)
load(mongo)
load(one)
one.js
load(two)
two.js
load(three)
three.js
load(null)
main.js

As you can see, the last few lines show that the packages were loaded in the expected order, i.e., one, two, three.

@radekmie radekmie requested review from zodern and denihs April 15, 2024 20:16
@radekmie radekmie added this to the Release 3.0 milestone Apr 15, 2024
Copy link

netlify bot commented Apr 15, 2024

βœ… Deploy Preview for v3-meteor-api-docs canceled.

Name Link
πŸ”¨ Latest commit 37c2f09
πŸ” Latest deploy log https://app.netlify.com/sites/v3-meteor-api-docs/deploys/662158c46ca9b20008d850b8

@radekmie
Copy link
Collaborator Author

There's one test failing and I'm not sure if it's "failing correctly". The ordering is, of course, changed, but the final one looks OK to me. @zodern could you check it maybe?

diff --git a/tools/tests/top-level-await.js b/tools/tests/top-level-await.js
index b2deb6ca11..26d2fa0d4c 100644
--- a/tools/tests/top-level-await.js
+++ b/tools/tests/top-level-await.js
@@ -23,18 +23,18 @@ selftest.define("xxxx top level await - order", async function (options) {
 
     const lines = [
       'package sync',
-      'package 1 - b before',
-      'package 2 - b',
-      'package 2 - a before',
       'package sync - later',
+      'package 1 - b before',
       'package 1 - b after',
-      'package 2 - b later',
-      'package 2 - a after',
       'package 1 - b later',
       'package 1 - a before',
-      'package 2 - a later',
       'package 1 - a after',
       'package 1 - a later',
+      'package 2 - b',
+      'package 2 - a before',
+      'package 2 - b later',
+      'package 2 - a after',
+      'package 2 - a later',
       'app a.js - before',
       'app b.js - before',
       'app a.js - after',

@denihs
Copy link
Contributor

denihs commented Apr 16, 2024

Well, considering that we now have a sequential load, I believe this new other is correct. Let's see what @zodern thinks.

denihs
denihs previously approved these changes Apr 16, 2024
@zodern
Copy link
Collaborator

zodern commented Apr 16, 2024

I'm a little surprised this works without removing the deps tracking, though it was designed to load in the original order when all packages are sync.

A couple things:

  1. As far as I know, Meteor 3 doesn't drop support for any browsers. If you notice, the code is careful to only use Promise after a package that uses top level await since that would indicate a promise polyfill has already loaded.
  2. package sync - later should be after package 1 - b before and before package 1 - b after. The top level await spec and the implementation in Meteor is very precise about when microtasks are added, and the core-runtime is the same to avoid breaking changes in Meteor 3 - when packages are sync, they run immediately after each other before any other microtasks are able to run.

Another thing is that the deps code isn't adding anything here. You should be able to move the queueing into core-runtime.queue and have it ignore the deps.

@zodern
Copy link
Collaborator

zodern commented Apr 16, 2024

I noticed I misunderstood the implementation. It seems it does rely on the deps code still, and only ensures sequential order for eager modules. It seems likely there are edge cases when mixing packages that are async with packages that don't have a dependency on modules or that have bare files.

In the logs, it needs an additional message for when a package finishes loading to check that each package doesn't start loading until the previous one finishes.

I would also suggest making package 1 in the test use top level await to wait 100ms before logging console.log('one.js'), and adding a bare file to one of the other packages.

@zodern
Copy link
Collaborator

zodern commented Apr 16, 2024

The after logs don't show the packages loading in the correct order:

queue(meteor, [core-runtime])
load(meteor)
queue(meteor-base, [meteor])
load(meteor-base)
queue(npm-mongo, [meteor])
load(npm-mongo)
queue(modules-runtime, [meteor])
load(modules-runtime)
queue(modules, [meteor,modules-runtime])
load(modules)
queue(react-fast-refresh, [meteor,modules])
queue(ecmascript, [meteor,react-fast-refresh])
queue(ecmascript-runtime, [meteor])
load(ecmascript-runtime) <---- this package is loading before react-fast-refresh

The packages that have a dependency on modules (have eager modules) are delayed and run later than they should. If you look in the before logs, the packages run immediately after being queued until it gets to a package that uses top level await.


I was expecting the implementation to look something like:

var pending = [];
var processingQueue = false;

function queue(name, runImage) {
 pending.push({name: name, runImage: runImage});

  processNext();
}

function processNext() {
  if (processingQueue) { 
    return;
  }
 
  var config = pending.shift();
  if (!config) {
    return;
  }

  processingQueue = true;
  loadImage(config.name, config.runImage, function () {
    processingQueue = false;
    processNext();
  });
}

It has a simple queue and processes one item at a time. Since queue is already called in the correct order for packages to load, it preserves that order and no longer needs the deps array.

@radekmie
Copy link
Collaborator Author

radekmie commented Apr 16, 2024

I've tried to implement such a queue @zodern, but no matter what I try, it fails with the following error while loading the promise package:

Code
// 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 hasOwn = Object.prototype.hasOwnProperty;

var pending = [];
var isProcessing = false;
function queue(name, deps, runImage) {
  console.log(`queue(${name})`);
  pending.push({name: name, runImage: runImage});
  processNext();
}

function processNext() {
  if (isProcessing) {
    return;
  }

  var next = pending.shift();
  if (!next) {
    return;
  }

  console.log(`load(${next.name})`);
  isProcessing = true;

  var config = next.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(next.name, mainModuleExports, exports);
    } else {
      Package._define(next.name, exports);
    }

    isProcessing = false;
    processNext();
  });
}

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);
    if (checkAsyncModule(exports)) {
      if (path === config.mainModulePath) {
        mainModuleAsync = true;
      }

      // Is an async module
      exports.then(function (exports) {
        if (path === config.mainModulePath) {
          mainExports = exports;
        }
        evaluateNextModule();
      })
      // This also handles errors in modules and packages loaded sync
      // afterwards since they are run within the `.then`.
      .catch(function (error) {
        if (
          typeof process === 'object' &&
          typeof process.nextTick === 'function'
        ) {
          // Is node.js
          process.nextTick(function () {
            throw error;
          });
        } else {
          // TODO: is there a faster way to throw the error?
          setTimeout(function () {
            throw error;
          }, 0);
        }
      });
    } else {
      if (path === config.mainModulePath) {
        mainExports = exports;
      }
      evaluateNextModule();
    }
  }

  evaluateNextModule();
}

function checkAsyncModule (exports) {
  var potentiallyAsync = exports && typeof exports === 'object' &&
    hasOwn.call(exports, '__reifyAsyncModule');

  if (!potentiallyAsync) {
    return;
  }

  return typeof exports.then === 'function';
}

// 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() {
  if (pending.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) {
    queue('', [], function () {
      resolve();
      return {};
    });
  });
}

// 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
};
Errors prevented isopacket build:             

While loading plugin `compile-ecmascript` from package `ecmascript`:
packages/promise.js:60:21: Cannot read properties of undefined (reading 'prototype')
at module (packages/promise.js:60:21)
at fileEvaluate (packages/modules-runtime.js:335:7)
at Module.require (packages/modules-runtime.js:237:14)
at Module.mod.require
(/Users/radekmie/Projects/meteor/packages/modules/.npm/package/node_modules/@meteorjs/reify/lib/runtime/index.js:30:33)
at require (packages/modules-runtime.js:257:21)
at module (packages/promise.js:33:1)
at fileEvaluate (packages/modules-runtime.js:335:7)
at Module.require (packages/modules-runtime.js:237:14)
at Module.mod.require
(/Users/radekmie/Projects/meteor/packages/modules/.npm/package/node_modules/@meteorjs/reify/lib/runtime/index.js:30:33)
at Object.require (packages/modules-runtime.js:257:21)
at evaluateNextModule (packages/core-runtime.js:170:26)
at evaluateNextModule (packages/core-runtime.js:205:7)
at runEagerModules (packages/core-runtime.js:209:3)
at processNext (packages/core-runtime.js:131:3)
at Object.queue (packages/core-runtime.js:114:3)
at packages/promise.js:1:29


While loading plugin `compile-typescript` from package `typescript`:
packages/promise.js:60:21: Cannot read properties of undefined (reading 'prototype')
at module (packages/promise.js:60:21)
at fileEvaluate (packages/modules-runtime.js:335:7)
at Module.require (packages/modules-runtime.js:237:14)
at Module.mod.require
(/Users/radekmie/Projects/meteor/packages/modules/.npm/package/node_modules/@meteorjs/reify/lib/runtime/index.js:30:33)
at require (packages/modules-runtime.js:257:21)
at module (packages/promise.js:33:1)
at fileEvaluate (packages/modules-runtime.js:335:7)
at Module.require (packages/modules-runtime.js:237:14)
at Module.mod.require
(/Users/radekmie/Projects/meteor/packages/modules/.npm/package/node_modules/@meteorjs/reify/lib/runtime/index.js:30:33)
at Object.require (packages/modules-runtime.js:257:21)
at evaluateNextModule (packages/core-runtime.js:170:26)
at evaluateNextModule (packages/core-runtime.js:205:7)
at runEagerModules (packages/core-runtime.js:209:3)
at processNext (packages/core-runtime.js:131:3)
at Object.queue (packages/core-runtime.js:114:3)
at packages/promise.js:1:29


/Users/radekmie/Projects/meteor/tools/tool-env/isopackets.js:234
    throw new Error("isopacket build failed?");
          ^

Error: isopacket build failed?
    at Object.ensureIsopacketsLoadable (/tools/tool-env/isopackets.js:239:11)
    at /tools/cli/main.js:869:3

Node.js v20.11.1

I'll try to make my Promise-based solution to match the ordering you said and think on how to handle it without Promises.

@denihs denihs merged commit 2b87403 into release-3.0 Apr 19, 2024
@radekmie radekmie deleted the sequential-packages-loading-order branch April 19, 2024 14:53
var pendingNames = Object.keys(pending);

if (pendingNames.length === 0) {
if (pending.length === 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to check if isProcessing is true. Otherwise, it won't wait when the only js image that is async is the last one.

Besides this, it looks good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@denihs can you add this check or should I file another PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@radekmie please open a new PR. Then, next week, I can merge it and do another release with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants