Node.js · API

Plugin

Un plugin extiende DON.parse recorriendo cada directiva mientras se construye el árbol. Sirve para transformar argumentos (por ejemplo, resolver variables) o eliminar directivas.

interface PluginDirectiveNode {
  readonly name: string | symbol;
  readonly args: readonly (number | string | boolean | HeredocValue)[];
  readonly children?: readonly PluginDirectiveNode[];
}

interface DonPlugin<TContext = unknown> {
  readonly name: string;
  initContext?(): TContext;
  onDirective?(node: PluginDirectiveNode, ctx: TContext):
    PluginDirectiveNode | null | void;
  onEnterScope?(node: PluginDirectiveNode, ctx: TContext): void;
  onExitScope?(node: PluginDirectiveNode, ctx: TContext): void;
}

Hooks

Transformar y eliminar

import { DON, type DonPlugin } from "donly";

const upper: DonPlugin = {
  name: "upper",
  onDirective(node) {
    if (node.name === "drop") return null; // elimina la directiva
    if (node.name === "name") {
      return {
        ...node,
        args: node.args.map((a) => (typeof a === "string" ? a.toUpperCase() : a)),
      };
    }
  },
};

JSON.stringify(DON.parse('name "x"\ndrop 1\nport 1', { plugins: [upper] }));
// {"name":"X","port":1}

Orden de los hooks

const trace: DonPlugin<{ depth: number }> = {
  name: "trace",
  initContext: () => ({ depth: 0 }),
  onDirective: (node) => log.push("dir " + String(node.name)),
  onEnterScope: (node, ctx) => log.push("in " + String(node.name) + ++ctx.depth),
  onExitScope: (node, ctx) => log.push("out " + String(node.name) + ctx.depth--),
};

DON.parse("a { b { c } }\nd", { plugins: [trace] });
// dir a, in a1, dir b, in b2, dir c, in c3,
// out c3, out b2, out a1, dir d, in d1, out d1

Plugins incluidos

scoped-variables implementa set, $nombre y ${nombre} con alcance por bloque.