en DslContext - chiba233/yumeDSL GitHub Wiki

DslContext

Exports | Source Position Tracking

DslContext is a tiny context object that bundles "which syntax are we using" and "how to generate token IDs" together. The parser passes it to your handler as the last argument — all you need to do is forward it to utility functions.


Where it shows up

createParser / parseRichText
        │
        │  Internally creates DslContext
        │  { syntax, createId }
        │
        ▼
  Your handler(tokens, ctx)        ← ctx is DslContext
        │
        │  Forward ctx
        ▼
  parsePipeArgs(tokens, ctx)       ← uses ctx.syntax to know the pipe character
  materializeTextTokens(tokens, ctx) ← uses ctx.syntax to know how to unescape
  createTextToken(value, ctx)      ← uses ctx.createId to generate IDs

Interface

interface DslContext {
    syntax: SyntaxConfig;   // active syntax configuration
    createId?: CreateId;    // token ID generator (optional)
}

Just two fields. Very lightweight. Here's what it looks like if you log it:

// console.log(ctx) inside a handler:
{
    syntax: {
        tagPrefix: "$$",
        tagOpen: "(",
        tagClose: ")",
        tagDivider: "|",
        endTag: ")$$",
        rawOpen: ")%",
        blockOpen: ")*",
        rawClose: "%end$$",
        blockClose: "*end$$",
        escapeChar: "\\",
        escapableTokens: ["%end$$", "*end$$", ")$$", ")%", ")*", "(", ")", "|", "\\"],
    },
    createId: [Function],   // generates token IDs
}

It just tells your callback "which syntax tokens are active" and "how to generate IDs".


When do you need to care

Scenario What to do
Writing a handler (most common) Just forward ctx to parsePipeArgs and friends
Calling utilities outside a parse Construct one manually: { syntax: createSyntax(), createId: ... }
Using createParser without custom handlers Don't need to think about it at all
Old handler that doesn't accept ctx Still works (JS ignores extra args), but pipe splitting and escaping fall back to module defaults. Switch to custom syntax and handlers silently use the wrong delimiters — adding ctx is a one-line fix

Do I need to declare ctx in my callback?

Always declare it. Whether or not your callback body uses ctx, include it in the parameter list:

// Raw TagHandler
inline: (tokens, ctx) => ...
raw: (arg, content, ctx) => ...

// createPipeHandlers callback
inline: (args, ctx) => ...
raw: (args, content, ctx) => ...

Why:

  • ctx is guaranteed provided by the framework — declaring it has zero cost
  • A future major version will make ctx required; declaring it now means no migration work later
  • In concurrent environments (e.g., SSR), explicit ctx avoids reliance on module-level ambient state and ensures each request stays independent

Standard pattern in a handler

link: {
    inline: (tokens, ctx) => {
        const args = parsePipeArgs(tokens, ctx);  // ← forward ctx
        return { type: "link", url: args.text(0), value: args.materializedTailTokens(1) };
    },
}

Using outside a parse

const ctx: DslContext = {
    syntax: createSyntax(),
    createId: (draft) => `demo-${draft.type}`,
};
const args = parsePipeTextArgs("ts | Demo", ctx);

Fallback chain when ctx is omitted

Utility functions fall back in this order when they don't receive ctx:

  1. Syntax-related functions (splitting, escaping, etc.) → getSyntax() (module default syntax)
  2. createToken → module-level counter rt-0, rt-1, ...

As long as you haven't changed the default syntax, omitting ctx won't break anything. But a future major version will make ctx required — best to get in the habit of forwarding it now.