worker/registry.js

// Convention: createFooBar is exported from ./create_foo_bar.js (one level up from
// this directory). Only "create*" names are allowed through, since the function name
// is used to build a dynamic import path.
const NAME_PATTERN = /^create[A-Z]\w*$/;

function toFileName(functionName) {
  return functionName.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
}

/**
 * Dynamically imports the module for a create* function and invokes it with args.
 * Runs identically on the main thread or inside a worker.
 *
 * @param {String} functionName e.g. "createLocation"
 * @param {Object} [args] the single named-parameters object the function expects
 * @returns {*} whatever the create* function returns
 */
export async function callCreate(functionName, args) {
  if (!NAME_PATTERN.test(functionName)) {
    console.error(`${functionName} is not a create* function`);
    return null;
  }
  const fileName = toFileName(functionName);
  const module = await import(`../${fileName}.js`);
  const fn = module[functionName];
  if (typeof fn !== "function") {
    console.error(`${fn} is not a function`);
    return null;
  }
  const result = fn(args);
  return result;
}

/**
 * Expands a shimmed location (see createLocation's `shim` option) in place. Must run in the
 * same worker that originally created the location in order to find a LocationTemplate by
 * the location’s GUID (this may change).
 *
 * @param {Object} location a Location, possibly with descendants, produced by createLocation
 * @returns {Object} the same location, fully expanded
 */
export async function callExpand(location) {
  const { expandShim } = await import("../create_location.js");
  return expandShim(location);
}