The declarations below reproduce the exported custom-tag types from @mxlang/core’s custom-tags.ts. Referenced IR types (Attr, AttributeTag, Block, Expr, ForSource, IrNode, and Position) are also exported by @mxlang/core.
The exported limits are MAX_EXPANSION_DEPTH = 64 nested calls and MAX_EXPANSION_NODES = 100_000 nodes from one transform or finalize result.
Member guide
CustomTag
Member
Meaning
parseOptions
Static parser behavior that discovery reads before parsing the caller.
attributes
Closed attribute contract, validated before hooks; omit it to leave attributes open.
attributeTags
Closed <@name> contract with required/repeatable controls and recursive attributes, attributeTags and children maps.
defaultTag
The tag name that <#id> and <.class> (an unnamed tag) written directly inside this tag stand for. Also accepted on every attributeTags declaration. A non-empty string naming a plain tag reachable from the package; see Name the unnamed tag.
analyze
Non-mutating pass over every call of this tag in one file, before transforms.
transform
Expands one validated call into ordinary IR; optional only when a template exists.
finalize
Adds nodes once per file after expansion; returned nodes are prepended.
Parse and declaration members
Member
Meaning
text
Parses the body as one unparsed text node.
preserveWhitespace
Keeps body whitespace.
openTagOnly
Forbids a body and closing tag.
type
Checks a scalar literal type, a literal array ("array"), or a function ("function"), or requires expression syntax.
items
With type: "array", checks each literal element as "string", "number" or "boolean".
required
Requires the attribute or attribute tag.
enum
Restricts an attribute to listed string literals.
default
Supplies an omitted string, number, or boolean attribute on the custom tag after validation; not applied to attribute-tag attributes (decision 138 E4).
literalOnly
Requires a compile-time scalar, array, or object literal.
repeatable
Allows an attribute tag name to appear more than once.
TagCall
Member
Meaning
name
Tag name as written at the call.
loc
Call-site position.
attrs
Resolved attributes in source order, with defaults appended.
content
Ordinary body block, or null.
attributeTags
Resolved named blocks; repeated entries remain separate.
params
Tag parameter source text.
var
/var binding source text, or null.
/var on a custom tag call (e.g. <counter/n start=1/>) binds the value the
tag’s template hands back with <return> — see
Returning a value. A tag with no template
at all (a sidecar that builds IR) has no <return> to bind, so /var on one
is a positioned compile error.
Contexts and store
Member
Meaning
build
Position-aware constructors for the IR available in that phase.
gensym
Produces a hygienic file-unique binding, optionally using a hint.
fail
Creates a positioned TranslateError; always write throw ctx.fail(...).
hoist
Moves code to the head of the nearest enclosing function.
store
Private per-tag, per-file state shared by analyze, transform, and finalize.
get
Reads one typed store key.
set
Writes one store key.
Builders
Member
Meaning
text
Creates literal text.
interpolation
Creates escaped interpolation by default; false requests raw output.
element
Creates an element, optionally marked void.
attr
Creates a static string attribute.
dynamicAttr
Creates an expression-valued attribute.
booleanAttr
Creates a valueless boolean attribute.
expr
Parses expression source into an Expr.
ifChain
Creates ordered conditional/fallback branches.
forLoop
Creates an IR loop with source, params, bindings, key, and children.
block
Creates a child block and its optional params.
delegatedTag
Requests a primitive by name from the active host.
template
Expands this tag’s adjacent L1 template with the supplied call.
Positioned errors
Messages begin with the relevant tag name unless the problem belongs to a discovery file. Dynamic values such as names, paths, counts, expected types, and thrown messages are inserted into the forms below.
Registration and phase errors
Trigger
Diagnostic form
A registration uses a core-owned name such as try.
`<name>` is a core-owned custom tag and cannot be shadowed by a registered custom tag of the same name
An attributes/attributeTags declaration has an unknown key (e.g. the retired staticOnly/repeated).
Unknown key "KEY" in the "NAME" attribute declaration of tag "TAG"; allowed: …
A definition declares only finalize.
`<name>`: a custom tag that defines only `finalize` has no call site and nothing to collect…
A called tag has neither a transform nor a template.
`<name>`: custom tag has neither a `transform` nor a template file…
Nested custom-tag calls exceed 64.
`<name>`: custom tag expansion exceeded 64 nested invocations
A transform does not return an array.
`<name>`: custom tag must return an array of IR nodes
A transform returns more than 100,000 nodes.
`<name>`: custom tag expansion produced N nodes, over the 100000 limit
finalize does not return an array.
`<name>`: `finalize` must return an array of IR nodes
finalize returns more than 100,000 nodes.
`<name>`: `finalize` produced N nodes, over the 100000 limit
analyze or finalize throws something other than TranslateError.
`<name>`: custom tag `<hook>` threw: message
transform throws something other than TranslateError.
`<name>`: custom tag threw: message
Attribute errors
Trigger
Diagnostic form
A tag declaring attributes: {} receives any named or spread attribute.
`<name>`: accepts no attributes
A closed non-empty contract receives a spread.
`<name>`: spread attributes cannot be checked against this tag's declared attributes
An undeclared named attribute is present.
`<name>`: unknown attribute `x`
A literalOnly value is not compile-time static.
`<name>`: attribute `x` must be a literal
A scalar literal has the wrong declared type.
`<name>`: attribute `x` must be TYPE, got TYPE
type: "expression" receives static-string or valueless syntax.
`<name>`: attribute `x` must be an expression
An enum receives a non-literal expression.
`<name>`: attribute `x` must be a static value from …
An enum receives a non-string literal.
`<name>`: attribute `x` must be a string from …, got TYPE
An enum string is not a member.
`<name>`: attribute `x` must be one of …, got VALUE
A required attribute is absent.
`<name>`: missing required attribute `x`
A declared default is not a string, number, or boolean.
`<name>`: attribute `x` declares a `default` that … has no attribute spelling
Attribute-tag errors
Trigger
Diagnostic form
A closed contract receives an undeclared attribute tag.
`<name>`: unknown attribute tag `<@x>`
A name repeats without repeatable: true.
`<name>`: attribute tag `<@x>` may not be repeated
A required attribute tag is absent.
`<name>`: missing required attribute tag `<@x>`
Builder boundary errors
Trigger
Diagnostic form
delegatedTag is called from finalize.
`<name>`: `ctx.build.delegatedTag` is not available in `finalize`
The active host does not claim a requested primitive.
`<name>`: this host does not claim `<primitive>`, so a custom tag cannot emit one
template is called from finalize.
`<name>`: `ctx.build.template` is not available in `finalize`
template(call) is used without an adjacent template file.
`<name>`: this tag has no template file…
Template errors
Trigger
Diagnostic form
A template call supplies the content attribute.
`<name>`: `content` is reserved on a template tag; it names the body slot
A template call passes <@content>.
`<@content>` is reserved for the body of `<name>`
A call passes a body to an openTagOnly tag.
`<name>`: does not accept content
Compiling the template itself fails.
The underlying positioned compiler message, against the template’s own file.
A template is a separate module, so the cycle, spread, bare-input and
import-collision errors the inlining model needed no longer exist: recursion is
ordinary ESM, a spread is an ordinary object, input is a parameter, and a
template’s imports never enter the caller’s module.
Discovery and sidecar errors
Trigger
Diagnostic form
mx.tags is neither a string nor the documented array shape.
A positioned manifest error describing the required shape.
An entry lacks string dir, has non-string prefix, or has non-string-array hosts.
A positioned manifest error naming the exact entry/member.
parseOptions is not a plain object, uses an unknown key, a non-boolean value, spread, or computed key.
A positioned sidecar/manifest error naming the exact option or unreadable shape.
A sidecar cannot be parsed.
FILE: could not be parsed: message
A sidecar throws while loading.
FILE: sidecar failed to load: message, with a top-level-await or explicit-extension hint when recognized.
A sidecar’s default export is not an object.
FILE: sidecar must `export default` a CustomTag object
A non-dot tag filename has an unusable basename.
FILE: NAME is not a usable tag name…
A scanned .solid.mx is mistaken for a tag template.
FILE: tag templates are .mx; .solid.mx is not supported as a tag
Warnings and non-fatal diagnostics
Trigger
Warning form
A real transform receives attribute tags but never reads call.attributeTags.
`<name>`: custom tag transform did not read its attributeTags; authored attribute tags were dropped
An L1 caller supplies a body but the template has no content placeholder.
`<name>`: body content was dropped; FILE has no `<${input.content}/>` placeholder
An L1 caller supplies <@x> but the template has no matching placeholder.
`<name>`: `<@x>` was dropped; FILE has no `<${input.x.content}/>` placeholder
mx.tags names a missing directory.
`mx.tags` names a directory that does not exist: PATH
A tag file tries to redefine a core-owned name.
`<name>` is a core-owned custom tag and cannot be redefined by a tag file; rename this file
The last two are scanner diagnostics: the bad entry is skipped and discovery continues. The language server surfaces them as warnings; build integrations warn once per distinct problem.