'Imported module output flushed on exit only

I've been trying to build a plugin system for my NodeJS application. So it does not run in a DOM/browser context.

The code to import the module is:

const loadAsyncImport = () => {
  const asyncImport = async () => {
    return await myDynamicImport(pluginMainPath);
  };

  return asyncImport().then((result) => {
    return result;
  });
};

const myDynamicImport = async (path) => {
  return await import(path);
};
loadAsyncImport().then(value => {
  pluginData = value['default'].initializePlugin();
  console.log('pluginData is: ' + pluginData);
});

Here is the plugin code:

function initializePlugin() {
  console.log('BEGIN main.bootStrapPlugin function');
  let returnData = 'HELLO WORLD - FROM PluginOne';
  console.log('END main.bootStrapPlugin function');
  return returnData;
};

export default {
  initializePlugin
};

This app has a main program loop that accepts input and executes commands. One command is "X" that will exit the CLI interface main program loop and terminate the Node process. I have the code that loads the plugin in the bootStrapper so it executes well before the program loop even begins. However, the output from the plugin doesn't seem to get set until after I exit the application. I've tried multiple (await (await ()) style wrappers but nothing seems to allow for the execution to actually execute in-line with the application when it is actually executed. The rest of my code is synchronous.

Here is the output:

BEGIN testHarness.application Function

0.0.1
A test harness application to test the haystacks framework.
BEGIN controllers.warden.loadPlugin Function
pluginPath is: C:/haystacks-plugins/pluginOne/
resolvedPluginPath is: C:\haystacks-plugins\pluginOne\package.json
pluginMetaData is: {"name":"appplugin-one","version":"0.0.1","description":"An example plugin 
implementation for the haystacks platform.","main":"./src/main.js","type":"module","scripts": 
{"test":"echo \"Error: no test specified\" && exit 1"},"repository": 
{"type":"git","url":"git+https://github.com/SethEden/pluginOne.git"},"keywords": 
["NodeJS","Plugin","NodeJS-App"],"author":"Seth Hollingsead","license":"MIT","bugs": 
{"url":"https://github.com/SethEden/AppPlugin- 
One/issues"},"homepage":"https://github.com/SethEden/AppPlugin-One#readme"}
pluginMainPath is: ./src/main.js
pluginMainPath is: C:\haystacks-plugins\pluginOne\src\main.js
pluginMainPath is: file:///C:/haystacks-plugins/pluginOne/src/main.js
// ------->>>>>          <---- This is where the output from the plugin loading should be. 
returnData is: false
END controllers.warden.loadPlugin Function
BEGIN main program loop
BEGIN command parser
>x                       // <---- User entry to exit the application
END command parser
END main program loop
Exiting TEST HARNESS APPLICATION
END testHarness.application Function
BEGIN main.bootStrapPlugin function // <---- Plugin execution begins
END main.bootStrapPlugin function // <---- Plugin execution ends
pluginData is: HELLO WORLD - FROM PluginOne // <---- Data from plugin is finally logged

You can see in the output: pluginPath, resolvedPluginPath, pluginMetaData and pluginMainPath are all my attempts to load the package.json and extract the main path to the entry-point for the plugin. Then I load that path after resolving it to the URL.

Is there something with the CMD buffer or is it an order of operations thing? Something with call-stack or a child-process thing? Or is this really another async issue?

Is it something that can be solved with synchronous? I've tried to flush the CMD buffer and I've tried to drain the CMD buffer, and as I've said I've tried creating multiple async await wrappers to force it to resolve. What am I missing here?

Yes I know it is a blocking operation. It's just loading a plugin, I don't expect it to take more than 1 second, even that would be just fine given all of this is happening as a native application.

EDIT:

I have mocked up a completely empty project that reproduces the problem this is the package.json:

{
  "name": "ultraplugintest",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "type": "module",
  "scripts": {
    "test": "node main.js"
  },
  "author": "Seth Hollingsead",
  "license": "MIT"
}

This is the code for main.js:

import fs from 'fs';
import url from 'url';
import path from 'path';

function importPlugin(pluginPath) {
  console.log('BEGIN main.importPlugin function');
  console.log(`pluginPath is: ${pluginPath}`);
  let resolvedPluginPath = path.resolve(pluginPath + '/package.json');
  console.log('resolvedPluginPath is: ' + resolvedPluginPath);
  let pluginMetaData = getJsonData(resolvedPluginPath, '');
  console.log('pluginMetaData is: ' + JSON.stringify(pluginMetaData));
  let pluginMainPath = pluginMetaData['main'];
  console.log('pluginMainPath is: ' + pluginMainPath);
  pluginMainPath = path.join(pluginPath, pluginMainPath);
  console.log('pluginMainPath is: ' + pluginMainPath);
  pluginMainPath = url.pathToFileURL(pluginMainPath);
  console.log('pluginMainPath is: ' + pluginMainPath);
  let importedModule;
  let pluginData;

    const loadAsyncImport = () => {
      const asyncImport = async () => {
        return await myDynamicImport(pluginMainPath);
      };

      return asyncImport().then((result) => {
        return result;
      });
    };

    const myDynamicImport = async (path) => {
      return await import(path);
    };
    loadAsyncImport().then(value => {
    pluginData = value['default'].initializePlugin();
    console.log('dataLoaded is: ' + pluginData);
  });
  console.log('plugin data should be fully loaded by now and...');
  console.log('the execution log should show that it was executed before 
  this line.');
  console.log('END main.importPlugin function');
};

/**
 * @function getJsonData
 * @description Loads the specified file and parses it into a JSON 
 * object(s).
 * @param {string} inputData The path and file name of the JSON file
 * that
 * should be loaded and parsed into JSON objects.
 * @param {string} inputMetaData Not used for this business rule.
 * @return {object} The JSON object as it was loaded from the file with
 * minimal to no additional processing.
 * @author Seth Hollingsead
 * @date 2022/04/28
 * @NOTE Cannot use the loggers her, because of a circular dependency.
 */
const getJsonData = function(inputData, inputMetaData) {
  let functionName = getJsonData.name;
  console.log(`BEGIN main.${functionName} function`);
  console.log(`inputData is: ${inputData}`);
  console.log(`inputMetaData is: ${inputMetaData}`);
  // Make sure to resolve the path on the local system,
  let pathAndFilename = path.resolve(inputData);
  let rawData = fs.readFileSync(pathAndFilename, { encoding: 'UTF8' });
  let returnData = JSON.parse(rawData);
  console.log(`DONE loading data from: ${inputData}`);
  console.log(`returnData is: ${JSON.stringify(returnData)}`);
  console.log(`END main.${functionName} function`);
  return returnData;
};

console.log('BEGIN main.js');
importPlugin('C:/haystacks-plugins/pluginOne/');

console.log('END main.js');

And running npm test gives this output, same as my other project and code:

C:\ultraPluginTest>npm test

> [email protected] test
> node main.js

BEGIN main.js
BEGIN main.importPlugin function
pluginPath is: C:/haystacks-plugins/pluginOne/
resolvedPluginPath is: C:\haystacks-plugins\pluginOne\package.json
BEGIN main.getJsonData function
inputData is: C:\haystacks-plugins\pluginOne\package.json
inputMetaData is:
DONE loading data from: C:\haystacks-plugins\pluginOne\package.json
returnData is: {"name":"appplugin
one","version":"0.0.1","description":"An example plugin implementation
for the haystacks
platform.","main":"./src/main.js","type":"module","scripts"
{"test":"echo \"Error: no test specified\" && exit 1"},"repository}
{"type":"git","url":"git+https://github.com/SethEden/pluginOne.git"},
"keywords":["NodeJS","Plugin","NodeJS-App"],"author":"Seth
Hollingsead","license":"MIT","bugs":
{"url":"https://github.com/SethEden/AppPlugin-
One/issues"},"homepage":"https://github.com/SethEden/AppPlugin-
One#readme"}
END main.getJsonData function
pluginMetaData is: {"name":"appplugin-
one","version":"0.0.1","description":"An example plugin implementation 
for the haystacks 
platform.","main":"./src/main.js","type":"module","scripts":
{"test":"echo \"Error: no test specified\" && exit 1"},"repository":
{"type":"git","url":"git+https://github.com/SethEden/pluginOne.git"},
"keywords":["NodeJS","Plugin","NodeJS-App"],"author":"Seth
Hollingsead","license":"MIT","bugs":
{"url":"https://github.com/SethEden/AppPlugin-
One/issues"},"homepage":"https://github.com/SethEden/AppPlugin-
One#readme"}
pluginMainPath is: ./src/main.js
pluginMainPath is: C:\haystacks-plugins\pluginOne\src\main.js
pluginMainPath is: file:///C:/haystacks-plugins/pluginOne/src/main.js
plugin data should be fully loaded by now and...
the execution log should show that it was executed before this line.
END main.importPlugin function
END main.js
BEGIN main.bootStrapPlugin function  <--- Again this line should have 
    been printed above
END main.bootStrapPlugin function  <--- Again this line should have been 
    printed above
dataLoaded is: HELLO WORLD - FROM PluginOne   <--- Again this line 
    should have been printed above


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source