concord-consortium/lara

View on GitHub
lara-typescript/src/plugins/plugins.ts

Summary

Maintainability
C
1 day
Test Coverage
import { IRegisterPluginOptions } from "../plugin-api";
import {
  IPluginContextOptions, IPluginRuntimeContextOptions, IPluginAuthoringContextOptions, generateRuntimePluginContext,
  generateAuthoringPluginContext
} from "./plugin-context";

const pluginError = (e: string, other: any) => {
  // tslint:disable-next-line:no-console
  console.group("LARA Plugin Error");
  // tslint:disable-next-line:no-console
  console.error(e);
  // tslint:disable-next-line:no-console
  console.dir(other);
  // tslint:disable-next-line:no-console
  console.groupEnd();
};

/** @hidden Note, we call these `classes` but any constructor function will do. */
const pluginClasses: { [label: string]: IRegisterPluginOptions } = {};

/**
 * Called in plugins/_show.haml before each plugin is loaded.  The value is used by #registerPlugin
 * to override the plugin's passed label.  This removes the previous need for the plugin's label to
 * match the label in LARA's database.
 */
let nextPluginLabel = "";
export const setNextPluginLabel = (override: string) => {
  nextPluginLabel = override;
};

/****************************************************************************
 Note that this method is NOT meant to be called by plugins. It's used by LARA internals.
 This method is called to initialize the plugin.
 Called at runtime by LARA to create an instance of the plugin as would happen in `views/plugin/_show.html.haml`.
 @param label The the script identifier.
 @param options Initial plugin context generated by LARA. Will be transformed into IPluginRuntimeContext instance.
 ****************************************************************************/
export const initPlugin = (label: string, options: IPluginContextOptions) => {
  if (options.type === "authoring") {
    initAuthoringPlugin(label, options);
  }
  else {
    initRuntimePlugin(label, options);
  }
};

const initRuntimePlugin = (label: string, options: IPluginRuntimeContextOptions) => {
  const Constructor = pluginClasses[label].runtimeClass;
  if (typeof Constructor === "function") {
    try {
      const plugin = new Constructor(generateRuntimePluginContext(options));
    } catch (e) {
      pluginError(e, options);
    }
    // tslint:disable-next-line:no-console
    console.info("Plugin", label, "is now registered");
  } else {
    // tslint:disable-next-line:no-console
    console.error("No plugin registered for label:", label);
  }
};

const initAuthoringPlugin = (label: string, options: IPluginAuthoringContextOptions) => {
  const Constructor = pluginClasses[label].authoringClass;
  if (typeof Constructor === "function") {
    try {
      const plugin = new Constructor(generateAuthoringPluginContext(options));
    } catch (e) {
      pluginError(e, options);
    }
    // tslint:disable-next-line:no-console
    console.info("Plugin", label, "is now registered");
  } else {
    // tslint:disable-next-line:no-console
    console.error("No plugin registered for label:", label);
  }
};

/****************************************************************************
 Register a new external script
 ```
 registerPlugin({runtimeClass: DebuggerRuntime, authoringClass?: DebuggerAuthoring})
 ```
 @param options The registration options
 @returns `true` if plugin was registered correctly.
 ***************************************************************************/
export const registerPlugin = (options: IRegisterPluginOptions): boolean => {
  if (nextPluginLabel === "") {
    // tslint:disable-next-line:no-console
    console.error("nextPluginLabel not set via #setNextPluginLabel before plugin loaded!");
    return false;
  }
  const {runtimeClass, authoringClass} = options;
  if (typeof runtimeClass !== "function") {
    // tslint:disable-next-line:no-console
    console.error("Plugin did not provide a runtime constructor", nextPluginLabel);
    return false;
  }
  if (typeof authoringClass !== "function") {
    // tslint:disable-next-line:no-console
    console.warn(`Plugin did not provide an authoring constructor. This is ok if "guiAuthoring"
                  is not set for this component.`, nextPluginLabel);
}
  if (pluginClasses[nextPluginLabel]) {
    // tslint:disable-next-line:no-console
    console.error("Duplicate Plugin for label", nextPluginLabel);
    return false;
  } else {
    pluginClasses[nextPluginLabel] = options;
    nextPluginLabel = "";
    return true;
  }
};