Server-side template rendering engine for MemberJunction, built on Nunjucks. Extends TemplateEngineBase with rendering capabilities, custom filters, and MJ-specific extensions for AI prompt integration and recursive template embedding.
graph TD
subgraph "@memberjunction/templates"
A[TemplateEngineServer] --> B[Nunjucks Environment]
A --> C[Template Cache]
A --> D[TemplateEntityLoader]
B --> E[Custom Filters]
B --> F[Extensions]
F --> G[AIPrompt Extension]
F --> H[TemplateEmbed Extension]
end
subgraph "Base Layer"
I["TemplateEngineBase
(from base-types)"]
end
A -->|extends| I
subgraph "Rendering"
J[Template Entity] --> K[Validation]
K --> L[Merge Defaults]
L --> M[Nunjucks Render]
M --> N[TemplateRenderResult]
end
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
style B fill:#2d8659,stroke:#1a5c3a,color:#fff
style C fill:#7c5295,stroke:#563a6b,color:#fff
style G fill:#b8762f,stroke:#8a5722,color:#fff
style H fill:#b8762f,stroke:#8a5722,color:#fff
style I fill:#2d6a9f,stroke:#1a4971,color:#fff
style N fill:#2d8659,stroke:#1a5c3a,color:#fff
This package is the server-side rendering engine for MemberJunction templates. It is NOT used within Angular applications despite using Angular Universal internally for compilation.
Key capabilities:
json, jsoninline, and jsonparse filters for JSON manipulationTemplateExtensionBase for custom Nunjucks tagsnpm install @memberjunction/templates
import { TemplateEngineServer } from '@memberjunction/templates';
const engine = TemplateEngineServer.Instance;
await engine.Config(false, contextUser);
// Find and render a template
const template = engine.FindTemplate('Welcome Email');
const content = template.Content[0]; // First content variant
const result = await engine.RenderTemplate(template, content, {
userName: 'John Doe',
companyName: 'Acme Corp'
});
if (result.Success) {
console.log(result.Output); // Rendered HTML/text
} else {
console.error(result.Message); // Validation or rendering error
}
For ad-hoc templates not stored in the database:
const result = await engine.RenderTemplateSimple(
'Hello {{ name }}, welcome to {{ company }}!',
{ name: 'Jane', company: 'MemberJunction' }
);
// result.Output: "Hello Jane, welcome to MemberJunction!"
By default, output is HTML-escaped. For plain-text contexts such as email subject lines — where
Acme & Co must not become Acme & Co — opt out with the autoescape option:
const subject = await engine.RenderTemplateSimple(
'{{ company }} — your invite',
{ company: 'Acme & Co' },
{ autoescape: false }
);
// subject.Output: "Acme & Co — your invite" (no entity encoding)
The engine provides built-in filters for JSON operations:
{# Convert object to formatted JSON #}
{{ userData | json }}
{# Compact JSON output #}
{{ userData | jsoninline }}
{# Parse a JSON string back to object #}
{% set parsed = jsonString | jsonparse %}
Users can edit stored templates, so the engine guards nunjucks when this package loads
(HardenNunjucksRuntime()). The guard is process-wide: it also covers nunjucks environments that
other packages create, and templates loaded through include and import. It checks templates at
two points.
Before a template compiles. The nunjucks compiler copies names from the template into the code it
generates, so AssertTemplateTreeIsSafe() checks every parsed template first:
set, macro, import and block names must be plain names: letters,
combining marks, digits, connector characters such as _, and $. A filter name can also contain
dots.is defined, is none) and an extension's registered name and method must not
contain a quote, a backslash or a line break.Object.prototype, such as toString,
hasOwnProperty or constructor), or prototype, must not be used as a dictionary key, a keyword
argument, a macro or call-block argument, a set, loop, import, macro or block name, or a filter or
test name. The code generator also refuses such a name as a dictionary key or keyword argument. Bare
names and member reads are not in this rule: {{ constructor }} reads only an own key of the render
data, and a member read such as obj.constructor or value.toLocaleString() goes through the
render-time member check (see below).obj["key"](), must not contain a backslash or a line break.context,
frame, runtime, env, cb, next, tasks or undefined. A unit test compiles every kind of
loop body with nunjucks 3.2.4, including loops inside macros and blocks, and checks this list against
the generated code, so a nunjucks upgrade that adds an identifier fails the test.While a template renders.
select and reject
filters receive while the template renders. Such a name must be text, a number or another simple
value such as none; an object, a function or a symbol is refused.constructor, prototype and the __define/lookup Getter/Setter__ accessors read
as empty unless they hold an object's own data that is not a function. __proto__ always reads as
empty. This applies to a literal member name (obj.name, obj["name"]) and to one computed while
the template renders (obj[key]) alike.{{ user }} resolves only to a template variable, an own key of the render data,
or an own environment global. It never resolves to a property that the data or the globals inherit,
such as valueOf.Function (or its async and generator variants), eval, Object,
Reflect, globalThis or process.A template that breaks one of these rules fails with a TemplateSandboxError.
The guard blocks reflection and code injection, not ordinary methods. Pass templates plain data, not
objects whose methods reach the database or the file system (for example BaseEntity instances).
The RunTemplate mutation and the Template.Run remote operation also apply
GetTemplateRunRefusal() before they load anything: they refuse scope-limited sessions and callers
without read permission on MJ: Templates and MJ: Template Contents.
Extensions are registered via the MJ class factory:
graph LR
A[TemplateExtensionBase] --> B[AIPrompt Extension]
A --> C[TemplateEmbed Extension]
A --> D[Custom Extensions]
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
style B fill:#2d8659,stroke:#1a5c3a,color:#fff
style C fill:#2d8659,stroke:#1a5c3a,color:#fff
style D fill:#7c5295,stroke:#563a6b,color:#fff
{% aiprompt %} tags{% templateembed %} tags| Member | Type | Description |
|---|---|---|
Instance |
static getter | Singleton instance |
Config() |
method | Load metadata and initialize Nunjucks environment |
RenderTemplate() |
method | Render a stored template with validation |
RenderTemplateSimple() |
method | Render an ad-hoc template string |
AddTemplate() |
method | Add a template to the Nunjucks loader |
SetupNunjucks() |
method | Re-initialize the Nunjucks environment |
ClearTemplateCache() |
method | Clear cached compiled templates |
TemplateRenderResult with Success, Output, and optional Message| Package | Purpose |
|---|---|
@memberjunction/templates-base-types |
Base engine and result types |
@memberjunction/core |
UserInfo, logging utilities |
@memberjunction/core-entities |
Template entity types |
@memberjunction/global |
Class factory for extensions |
@memberjunction/ai |
AI integration for prompts |
@memberjunction/ai-core-plus |
AI core utilities |
nunjucks |
Template rendering engine |
ISC