HomeDocumentationChatIssues GitHub

Extensibility

Definition of Terms

TermDefinitionLinks
ASTAbstract syntax tree. This is a hierarchical tree data-structure. Each node in the AST has a type, and each node type corresponds with a particular node shape. Meta-data about the node is also stored, including code location and comments.AST Explorer

Writing a Plugin

// Your plugin module should export a higher-order function.
// This function should accept as input an options object
// (this can be whatever you like), and return another
// function that closes over those options.
export default function (opts = {}) {

  // By convention, your exported function will be invoked
  // once, when your Interlock config file is evaluated.
  // Computations for which you already have all required
  // information should happen here.
  const someComputedOption = someComputation(opts);

  // The function you return here will be invoked at the
  // beginning of each compilation.  It will be passed
  // `override` and `transform` functions that you can
  // use to effect compilation behavior.
  return (override, transform) => {

    // If you want to override a compilation step:
    override("PLUGGABLE_NAME", input => {
      // If you want to skip your override, you can always...
      return override.CONTINUE;

      // Otherwise, do your computations here and...
      return someOutput;

      // If your behavior is asynchronous you can always...
      return Promise.resolve(someOutput);
    })

    // If instead you want to transform the default output:
    transform("PLUGGABLE_NAME", pluggableOutput => {
      // and ...
      return modifiedPluggableOutput;

      // Like `override`, you can always...
      return Promise.resolve(modifiedPluggableOutput);
    });
  };
}

 Start of Computation
 
End of computation 

This tool is a visual representation of the entire compilation process. Each arc above corresponds with a step of compilation; a pluggable. Arcs closer to the center point are higher-level calculations, and are composed of the adjacent arcs the next level out. Arcs on the outside have no pluggable dependencies.

As you hover over arcs, the name of the pluggable will be displayed. If you click on an arc, the visualization will zoom in and give you detailed information about that compilation step. To navigate back up the call stack, click the semi-circle at the center of the graph.

Plugin Context

If you're overriding step A, and A normally calls B, C, and D, you may want to do the same when you override.

This can be accomplished simply by invoking those function names on this.

override("pluggableA", function (inputA) {
  return this.pluggableB()
    .then(() => this.pluggableC())
    .then(() => this.pluggableD());
});

REMEMBER You'll often see arrow functions passed to override or transform. This is a great short-hand. However, keep in mind that arrow functions borrow their lexical scope's this context. If you expect to have access to this.someMethod() and it isn't there, check to see if you're using an arrow function.

You may also want to access the Interlock compilation options. These options are also accessible on the function context.

override("PLUGGABLE_NAME", function (inputA) {
  if (this.opts.someOption) {
    return "someValue";
  } else {
    return "someOtherValue";
  }
});

Run-time behavior

TODO

Best practices

Although it is not enforced, it is recommended to treat all input/output data-structures as immutable. For example, a method that returns a module with a new prop will usually be written as assign({}, module, { newProp }).

Learn More

To better understand the pieces of data that are flowing through compilation, read up on Interlock's build architecture.

TODO