sercrod

*import

Summary

*import loads HTML from an external URL and injects it into the current element’s innerHTML. The imported HTML is then processed by Sercrod in the same render pass, so any directives inside the imported content (*if, *for, *each, *include, *template, and so on) are evaluated as usual.

Key points:

Basic example

Load a simple partial file:

<serc-rod id="app" data='{"user":{"name":"Alice"}}'>
  <section *import="'/partials/user-card.html'"></section>
</serc-rod>

Behavior:

Behavior

At a high level, *import does the following when encountered on an element:

  1. It resolves a URL from the attribute value, using expression evaluation and simple heuristics.
  2. It checks the current include/import nesting depth and refuses to exceed a configured max_depth.
  3. It performs a synchronous HTTP request to download HTML from the resolved URL.
  4. If HTML is successfully obtained, it replaces the element’s innerHTML with the fetched HTML.
  5. It removes *import / n-import from the element.
  6. It does not return early after filling innerHTML so that normal child rendering (including directives inside the imported HTML) can run immediately.

Alias:

URL resolution

The value of *import is interpreted as a URL in two steps:

  1. Expression evaluation

    Sercrod first tries to evaluate the raw attribute value as an expression:

    • It calls eval_expr(raw_text, scope, { el: node, mode: "import", quiet: true }).
    • If the evaluation result is not null or undefined, Sercrod converts it to a string and trims it.
    • If the trimmed string is non-empty, that string becomes the URL.

    This allows patterns such as:

    <serc-rod id="app" data='{"base":"/partials","name":"user-card.html"}'>
      <section *import="base + '/' + name"></section>
    </serc-rod>
  2. Fallback to raw text

    If expression evaluation does not yield a usable string, Sercrod falls back to the raw attribute text and checks whether it “looks like a URL”:

    • The raw text must not contain whitespace.
    • If it starts with http:// or https://, or
    • If it starts with ./, ../, or /, or
    • If it contains a dot . or a slash /,

    then the raw text is treated as the URL.

If neither step produces a non-empty URL string, or the URL becomes the literal "false", the import is treated as invalid:

Network loading and caching

Once a URL is resolved, *import uses a synchronous XMLHttpRequest to fetch HTML:

Error handling:

Note:

Depth management and recursion guard

*import shares its depth tracking with *include:

Configuration:

Practical meaning:

Evaluation timing

Within the host rendering pipeline, *import runs after template registration and *include, but before literal-only blocks:

The *import directive itself is one-time per render pass:

Execution model

The internal steps for *import are roughly:

  1. Depth and configuration

    • Read include-related config (max_depth, warn_on_element, remove_element_if_empty).
    • Compute this element’s depth based on the nearest ancestor *include / *import.
  2. Depth check

    • If depth exceeds max_depth, handle overflow (mark the element if configured, remove directive, optionally keep the element) and stop.
  3. Resolve raw text

    • Read *import / n-import as raw_text.
    • If raw_text is empty, remove the attribute and stop.
  4. Resolve URL

    • Attempt expression evaluation for raw_text.
    • If that yields no usable string, treat raw_text as a potential URL and apply simple heuristics.
    • If no URL can be chosen or the result is "false", treat import as invalid, remove the directive, and stop (optionally marking the element).
  5. Fetch HTML

    • Use class-level cache for the URL.
    • If not cached yet, perform a synchronous HTTP request with configured method, headers, and credentials.
    • If response status is 2xx, treat responseText as HTML and cache it.
    • On error or exception, optionally mark the element, then stop if there is no HTML.
  6. Inject HTML and clean up

    • Assign node.innerHTML = html.
    • Remove *import / n-import attributes from the element.
    • Do not return here; instead, let the renderer continue to process the new children so that directives inside the imported HTML are executed.

Scope and interaction with imported HTML

*import does not create new variables or new scope layers by itself:

If you want imported content to have a specific scope:

*import itself does not inject any new names into the scope.

Use with *template, *include, and *each

*import is one of several structural directives that take control of a host element’s children:

Because they all try to own the same inner content, there are important restrictions:

  1. *import with *template on the same element

    • In the current implementation, *template is processed first and returns early.
    • That means *import on the same element never runs.
    • Treat this combination as unsupported; pick one or separate the responsibilities into different elements.
  2. *import with *include on the same element

    • *include and *import both want to replace the host’s innerHTML.
    • The runtime does not merge their behaviors.
    • Putting both on the same element is unsupported and leads to undefined behavior.
    • Use either *include (for local templates) or *import (for external HTML), but not both on one tag.
  3. *import with *each on the same element

    • *each expects to use the host’s original children as a loop body.
    • *import wants to overwrite those children with imported HTML.
    • The implementation does not coordinate these operations.
    • Therefore, a single element must not combine *each and *import. This combination is explicitly unsupported and should be avoided.

Supported pattern examples:

Configuration

*import reads settings from the Sercrod constructor’s config object:

These configuration values are optional; if you do not set them, Sercrod uses sensible defaults.

Best practices

Additional examples

Dynamic URL based on data:

<serc-rod id="app" data='{"lang":"en","page":"about"}'>
  <main *import="`/pages/${lang}/${page}.html`"></main>
</serc-rod>

Using headers and credentials via config (conceptual sketch):

Sercrod._config = Sercrod._config || {};
Sercrod._config.import = {
  method: "GET",
  credentials: true,
  headers: {
    "X-Requested-With": "XMLHttpRequest"
  }
};

Then:

<section *import="'/secure/partial.html'"></section>

Notes