<commit_before>section.glass.heading
  h1 Extensibility

section
  .content
    :markdown
      Whether you want to build a plugin or hack on Interlock directly, this is the place to start.  A few things first.

      **Interlock is built on pluggable functions.**  These pluggables are written like pure functions, taking inputs, delegating certain computations to other functions (often pluggables), and returning an output.  Through its plugin system, Interlock allows you to override or transform the behavior and output of any of the pluggables that are defined.

      **The tool below is a visual representation of the entire compilation process.**  Each visual arc 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 below.  **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.

      **If you already know what you'd like to override, scroll down to [Writing a Plugin](#writing-a-plugin).**

section.glass.interactive
  .interactive-docs
    .visualization

section.interactive
  .interactive-docs
    .status-bar-wrapper
      div
        span.ion-arrow-left-c &nbsp;
        span Start of Computation
      .status &nbsp;
      div
        span End of computation&nbsp;
        span.ion-arrow-right-c
    .doc-wrapper
      .doc

script(src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js")
script(src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/4.4.0/markdown-it.js")
script(src="/docs/js/interactive-docs-visualization.js")

section.glass#writing-a-plugin
  .content
    h2 Writing a Plugin

    pre.console.standalone
      code(class="language-javascript").
        // 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);
            });
          };
        }

section
  .content
    h2 Plugin Context

    .split
      .block
        :markdown
          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`.

      .block.fit
        pre.console
          code(class="language-javascript").
            override("pluggableA", function (inputA) {
              return this.pluggableB()
                .then(() => this.pluggableC())
                .then(() => this.pluggableD());
            });

    .side-note
      i.sigil.ion-alert
      :markdown
        **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 effect 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.

    hr

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

      .block.fit
        pre.console
          code(class="language-javascript").
            override("PLUGGABLE_NAME", function (inputA) {
              if (this.opts.someOption) {
                return "someValue";
              } else {
                return "someOtherValue";
              }<commit_msg>I accidentally a word.<commit_after>section.glass.heading
  h1 Extensibility

section
  .content
    :markdown
      Whether you want to build a plugin or hack on Interlock directly, this is the place to start.  A few things first.

      **Interlock is built on pluggable functions.**  These pluggables are written like pure functions, taking inputs, delegating certain computations to other functions (often pluggables), and returning an output.  Through its plugin system, Interlock allows you to override or transform the behavior and output of any of the pluggables that are defined.

      **The tool below is a visual representation of the entire compilation process.**  Each visual arc 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 below.  **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.

      **If you already know what you'd like to override, scroll down to [Writing a Plugin](#writing-a-plugin).**

section.glass.interactive
  .interactive-docs
    .visualization

section.interactive
  .interactive-docs
    .status-bar-wrapper
      div
        span.ion-arrow-left-c &nbsp;
        span Start of Computation
      .status &nbsp;
      div
        span End of computation&nbsp;
        span.ion-arrow-right-c
    .doc-wrapper
      .doc

script(src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js")
script(src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/4.4.0/markdown-it.js")
script(src="/docs/js/interactive-docs-visualization.js")

section.glass#writing-a-plugin
  .content
    h2 Writing a Plugin

    pre.console.standalone
      code(class="language-javascript").
        // 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);
            });
          };
        }

section
  .content
    h2 Plugin Context

    .split
      .block
        :markdown
          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`.

      .block.fit
        pre.console
          code(class="language-javascript").
            override("pluggableA", function (inputA) {
              return this.pluggableB()
                .then(() => this.pluggableC())
                .then(() => this.pluggableD());
            });

    .side-note
      i.sigil.ion-alert
      :markdown
        **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.

    hr

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

      .block.fit
        pre.console
          code(class="language-javascript").
            override("PLUGGABLE_NAME", function (inputA) {
              if (this.opts.someOption) {
                return "someValue";
              } else {
                return "someOtherValue";
              }
{'fraction_non_alphanumeric': 0.0780341217526173, 'fraction_numerical': 0.0021326095385808454, 'mean_word_length': 2.222048719550281, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4308039', 'n_tokens_mistral': 2622, 'n_tokens_neox': 2497, 'n_words': 1070}