52 lines
2.4 KiB
JavaScript
52 lines
2.4 KiB
JavaScript
/**
|
|
* Cold/shape-stable helpers shared by the mdast and hast visitors. The
|
|
* per-node hot decoders stay duplicated in each visitor on purpose: sharing
|
|
* them would feed differently-shaped arguments through one call site and turn
|
|
* it polymorphic.
|
|
*/
|
|
const EMPTY_BYTES = new Uint8Array(0);
|
|
export function asArray(value) {
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
/** Thrown when declarative replacement content can't be compiled to the
|
|
* structural op-stream — an unsupported node type (e.g. a bare `root`/`doctype`
|
|
* handed in as content) or an out-of-range numeric field. The op-stream is the
|
|
* only structural encoding, so this is a hard error rather than a fallback. */
|
|
export function unencodableContentError(content) {
|
|
const type = content?.type;
|
|
return new Error(`satteri: cannot encode replacement content${typeof type === "string" ? ` of type "${type}"` : ""} ` +
|
|
"into the structural op-stream — unsupported node type or out-of-range numeric field.");
|
|
}
|
|
/**
|
|
* Arena id for a node passed to a context method, via a per-kind `nid` lookup
|
|
* (closure keeps each kind's call site monomorphic). Plugin-built nodes have
|
|
* no id; without this check the id would coerce to 0 in the command buffer
|
|
* and the mutation would silently target the document root.
|
|
*/
|
|
export function makeRequireNid(nid) {
|
|
return (node, method) => {
|
|
const id = nid(node);
|
|
if (id === undefined) {
|
|
throw new Error(`${method}: node has no arena id — it was built in JS, not read from this tree. ` +
|
|
`Pass plugin-built nodes as new content (e.g. the second argument of insertAfter).`);
|
|
}
|
|
return id;
|
|
};
|
|
}
|
|
/** Concatenate the return-value and context command buffers for one pass,
|
|
* resetting both for reuse. */
|
|
export function mergeAndReset(returnBuffer, ctx) {
|
|
const ctxCmdBuf = ctx.getCommandBuffer();
|
|
// The common case — no mutations this pass — allocates nothing.
|
|
if (returnBuffer.length === 0 && ctxCmdBuf.length === 0) {
|
|
return { merged: EMPTY_BYTES, hasMutations: false };
|
|
}
|
|
const ctxBuf = ctxCmdBuf.getBuffer();
|
|
const retBuf = returnBuffer.getBuffer();
|
|
const merged = new Uint8Array(retBuf.length + ctxBuf.length);
|
|
merged.set(retBuf, 0);
|
|
merged.set(ctxBuf, retBuf.length);
|
|
returnBuffer.reset();
|
|
ctxCmdBuf.reset();
|
|
return { merged, hasMutations: true };
|
|
}
|