sercrod

*include

Summary

*include injects the inner content of a named template into the current element. The host element stays in the DOM as a single wrapper, and only its innerHTML is replaced. It has an alias n-include.

*include works with templates declared by *template (or n-template) in the current Sercrod world or ancestor worlds. After injecting the template content, Sercrod continues to process that content as normal children (conditions, loops, bindings, events, and so on).

Important structural restriction:

Basic example

A local template and an include:

<serc-rod id="app" data='{"user":{"name":"Alice","role":"admin"}}'>

  <!-- Template declaration -->
  <template *template="user-card">
    <article class="user-card">
      <h2 *print="user.name"></h2>
      <p *print="user.role"></p>
    </article>
  </template>

  <!-- Include: insert the template here -->
  <section>
    <div *include="user-card"></div>
  </section>

</serc-rod>

Behavior:

Behavior

Conceptually, *template and *include have these roles:

Key points:

Template name and expression syntax

The value of *include is a Sercrod expression that must resolve to a template name string.

Typical patterns:

Resolution rules (as implemented by _resolve_template_name):

  1. Sercrod first tries to evaluate the raw text as an expression in the current scope, in include mode.

    • If evaluation succeeds and yields a non-empty string, that string is used as the template name.
    • Errors such as ReferenceError are suppressed (quiet mode), so they do not print console warnings.
  2. If expression evaluation does not yield a usable name:

    • If the raw text looks like a simple identifier (letters, digits, underscore, hyphen), Sercrod uses it directly as the name.
    • Otherwise, the name resolution fails.
  3. If the final name is empty or no template with that name exists in any accessible world, *include fails and behaves as described in the error handling section below.

Practical guidance:

Template lookup and worlds

Templates are stored per Sercrod world:

Effect:

Evaluation timing

Within the rendering pipeline, *include is evaluated after *template registration and before normal child rendering of the host:

Important:

Execution model

At a high level, the implementation behaves as follows for a node node with *include or n-include:

  1. Determine include depth:

    • Sercrod uses an internal map _include_depth_map plus _get_nearest_include_depth(node) to compute the nesting depth of this *include relative to surrounding *include and *import directives.
    • If the depth exceeds the configured maximum (config.include.max_depth, default 16):
      • If config.include.warn_on_element is true, Sercrod marks the rendered element with sercrod-include-depth-overflow="<max_depth>".
      • It removes *include / n-include from the rendered element.
      • The include is skipped to prevent infinite recursion.
  2. If the *include value is empty:

    • Sercrod removes *include / n-include from the source node.
    • No template is inserted.
  3. Resolve the template name:

    • Sercrod calls _resolve_template_name(raw_text, scope, {el: node, mode: "include"}).
    • If name resolution fails (empty string):
      • If this.error?.warn is enabled:
        • Optionally marks the source node with sercrod-template-not-found="<raw_text>" when config.include.warn_on_element is true.
        • Removes *include / n-include from the source node.
      • The include is skipped.
  4. Find the template:

    • If the current world has a template registry and it contains name, that template is used.
    • Otherwise, _lookupTemplateNear(name) searches parent worlds.
    • If no template is found anywhere:
      • If this.error?.warn is enabled:
        • Optionally marks the rendered element with sercrod-template-not-found="<name or raw_text>" when config.include.warn_on_element is true.
        • Removes *include / n-include from the rendered element.
        • If config.include.remove_element_if_empty is false, the host element is still appended, potentially empty.
      • The include is skipped.
  5. Inject the template content:

    • If a template is found, Sercrod obtains its prototype proto.
    • It sets node.innerHTML = proto.innerHTML || "".
    • It removes *include / n-include from the rendered element.
    • It does not return early at this point: normal child rendering continues, now processing the included children.

This model keeps *include simple and predictable: name resolution, template lookup, then direct HTML injection followed by a standard child-rendering pass.

Variable creation and scope layering

*include does not create new variables or a new data layer.

If the template itself defines additional directives (for example *let):

Guidelines:

Parent access

Because *include does not introduce a new world or host, parent data access works in the usual way:

The only change is that the HTML you wrote under *template is now located under the *include host at render time.

Use with conditionals and loops

*include is designed to cooperate with *if, *for, and *each, as long as they are on different structural layers.

Host-level condition:

Host-level loops:

Child-level condition and loops:

Relationship with *import

*import is the I/O counterpart of *include:

For the injected HTML, the behavior is intentionally parallel:

Structural restrictions

Because *include, *each, and *import all want to control the host’s children, there are intentional restrictions:

Recommended patterns:

Comparison: *include vs *import

From the template system’s point of view:

Typical usage patterns:

The string passed to *import is treated purely as an URL from Sercrod’s perspective. If you use patterns like "/partials/card.html:card", the :card part is just part of the URL and is not parsed specially by Sercrod itself. Any such semantics (for example, serving only one named fragment from a combined file) must be implemented on the server side.

Best practices

Additional examples

Dynamic template selection:

<serc-rod data='{
  "mode": "compact",
  "cards": [1,2,3]
}'>
  <template *template="card-compact">
    <div class="card compact">
      <p>Compact card</p>
    </div>
  </template>

  <template *template="card-full">
    <div class="card full">
      <p>Full card with details</p>
    </div>
  </template>

  <section *for="id of cards">
    <div *include="mode === 'compact' ? 'card-compact' : 'card-full'"></div>
  </section>
</serc-rod>

Using a template as a partial inside a layout:

<serc-rod data='{"user":{"name":"Alice"}}'>

  <template *template="layout">
    <header>
      <h1>Sercrod demo</h1>
    </header>
    <main>
      <!-- Slot for content -->
      <div class="content">
        <!-- This area will be further filled by other includes or inline markup -->
      </div>
    </main>
    <footer>
      <small>Footer</small>
    </footer>
  </template>

  <div class="page" *include="'layout'">
    <!-- After include, the header/main/footer structure appears here -->
  </div>

</serc-rod>

Notes