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:
- Syntax-related functions (splitting, escaping, etc.) →
getSyntax()(module default syntax) - 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.