npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@bernierllc/template-validator

v1.0.3

Published

Atomic utility for validating template processing and variable substitution across multiple template engines

Downloads

12

Readme

@bernierllc/template-validator

Atomic utility package for validating template processing and variable substitution across multiple template engines.

Installation

npm install @bernierllc/template-validator

Features

  • Multi-Engine Support - Validates Liquid, Handlebars, Mustache, and EJS templates
  • Variable Detection - Detects unreplaced template variables
  • Syntax Validation - Validates template syntax and structure
  • Type Checking - Validates variable types against requirements
  • Scoring System - Provides 0-100 quality score for templates
  • Zero Dependencies - Pure TypeScript with no external dependencies (except @bernierllc/template-engine)
  • TypeScript First - Full type safety with comprehensive interfaces

Quick Start

import { validateTemplate, TemplateEngine } from '@bernierllc/template-validator';

const template = 'Hello {{ name }}, welcome to {{ site }}!';
const data = { name: 'Alice', site: 'Portal' };

const result = validateTemplate(template, data, TemplateEngine.LIQUID);

console.log(result.isValid);  // true
console.log(result.score);    // 100

API Reference

Core Functions

validateTemplate(template, data, engine?, options?)

Complete template validation with all checks.

interface TemplateValidationResult {
  isValid: boolean;
  unreplacedVariables: UnreplacedVariable[];
  syntaxErrors: TemplateSyntaxError[];
  missingVariables: MissingVariable[];
  conditionalIssues: ConditionalIssue[];
  score: number; // 0-100
}

function validateTemplate(
  template: string,
  data: Record<string, unknown>,
  engine?: TemplateEngine,
  options?: ValidationOptions
): TemplateValidationResult;

Parameters:

  • template - Template content to validate
  • data - Data object for variable checking
  • engine - Template engine (default: AUTO for auto-detection)
  • options - Validation options

Options:

interface ValidationOptions {
  strictMode?: boolean;           // Enable strict validation
  allowPartialTemplates?: boolean; // Allow templates with missing data
  customPatterns?: VariablePattern[]; // Custom variable patterns
}

Example:

const result = validateTemplate(
  '{{ user.name }} - {{ user.email }}',
  { user: { name: 'Alice', email: '[email protected]' } },
  TemplateEngine.LIQUID,
  { strictMode: true }
);

detectUnreplacedVariables(content, engines?, customPatterns?)

Detect all unreplaced template variables.

function detectUnreplacedVariables(
  content: string,
  engines?: TemplateEngine[],
  customPatterns?: VariablePattern[]
): UnreplacedVariable[];

Example:

import { detectUnreplacedVariables, TemplateEngine } from '@bernierllc/template-validator';

const vars = detectUnreplacedVariables(
  'Hello {{ name }}, email: {{ email }}',
  [TemplateEngine.LIQUID]
);

console.log(vars);
// [
//   { variable: 'name', pattern: '{{ name }}', location: {...} },
//   { variable: 'email', pattern: '{{ email }}', location: {...} }
// ]

validateTemplateSyntax(template, engine?)

Validate template syntax for structural errors.

function validateTemplateSyntax(
  template: string,
  engine?: TemplateEngine
): SyntaxValidationResult;

Example:

import { validateTemplateSyntax, TemplateEngine } from '@bernierllc/template-validator';

const result = validateTemplateSyntax(
  '{% if user %}{{ user.name }}{% endif %}',
  TemplateEngine.LIQUID
);

console.log(result.isValid);        // true
console.log(result.errors);         // []
console.log(result.supportedEngines); // [TemplateEngine.LIQUID]

validateRequiredVariables(template, data, requirements)

Validate that required variables are present and correctly typed.

function validateRequiredVariables(
  template: string,
  data: Record<string, unknown>,
  requirements: VariableRequirement[]
): MissingVariable[];

Example:

import { validateRequiredVariables } from '@bernierllc/template-validator';

const missing = validateRequiredVariables(
  'Hello {{ name }}',
  { age: 30 },
  [{ name: 'name', type: 'string', required: true }]
);

console.log(missing);
// [{ name: 'name', required: true, type: 'string' }]

extractVariableRequirements(template)

Extract variable requirements from template content.

function extractVariableRequirements(
  template: string
): VariableRequirement[];

Example:

import { extractVariableRequirements } from '@bernierllc/template-validator';

const requirements = extractVariableRequirements(
  '{{ user.name }} - {% if active %}{{ status }}{% endif %}'
);

console.log(requirements);
// [
//   { name: 'user.name', type: 'any', required: true },
//   { name: 'active', type: 'any', required: true },
//   { name: 'status', type: 'any', required: true }
// ]

Template Engines

enum TemplateEngine {
  LIQUID = 'liquid',      // {{ variable }}, {% if %}, {% for %}
  HANDLEBARS = 'handlebars', // {{variable}}, {{#if}}, {{#each}}
  MUSTACHE = 'mustache',  // {{variable}}, {{#section}}
  EJS = 'ejs',           // <%= variable %>
  AUTO = 'auto'          // Auto-detect from content
}

Usage Examples

Email Template Validation

import { validateTemplate, TemplateEngine } from '@bernierllc/template-validator';

const emailTemplate = `
  Dear {{ customer.name }},

  Your order #{{ order.id }} has been shipped!

  {% for item in order.items %}
    - {{ item.name }} x {{ item.quantity }}
  {% endfor %}

  Tracking: {{ shipping.trackingNumber }}

  Thank you,
  {{ company.name }}
`;

const data = {
  customer: { name: 'Alice Johnson' },
  order: {
    id: 'ORD-12345',
    items: [
      { name: 'Widget Pro', quantity: 2 },
      { name: 'Gadget Lite', quantity: 1 }
    ]
  },
  shipping: { trackingNumber: 'TRACK123' },
  company: { name: 'ACME Corp' }
};

const result = validateTemplate(emailTemplate, data, TemplateEngine.LIQUID);

if (result.isValid) {
  console.log('Template is valid! Score:', result.score);
} else {
  console.error('Template has issues:');
  console.error('Syntax errors:', result.syntaxErrors);
  console.error('Missing variables:', result.unreplacedVariables);
}

Detecting Missing Variables

import {
  detectUnreplacedVariables,
  filterMissingVariables,
  TemplateEngine
} from '@bernierllc/template-validator';

const template = 'Hello {{ name }}, email: {{ email }}, age: {{ age }}';
const data = { name: 'Bob' };

const allVars = detectUnreplacedVariables(template, [TemplateEngine.LIQUID]);
const missingVars = filterMissingVariables(allVars, data);

console.log('Missing variables:');
missingVars.forEach(v => {
  console.log(`  - ${v.variable} at line ${v.location.line}`);
});
// Output:
//   - email at line 1
//   - age at line 1

Type Validation

import { validateRequiredVariables } from '@bernierllc/template-validator';

const template = 'User #{{ userId }}: {{ userName }}';
const data = {
  userId: 'abc123',  // String instead of number!
  userName: 'Alice'
};

const requirements = [
  { name: 'userId', type: 'number', required: true },
  { name: 'userName', type: 'string', required: true }
];

const missing = validateRequiredVariables(template, data, requirements);

if (missing.length > 0) {
  console.log('Type mismatches:');
  missing.forEach(m => {
    console.log(`  - ${m.name} should be ${m.type}`);
  });
}
// Output: userId should be number

Handlebars Template Validation

import { validateTemplate, TemplateEngine } from '@bernierllc/template-validator';

const handlebarsTemplate = `
  <h1>Hello {{user.firstName}} {{user.lastName}}</h1>

  {{#if hasOrders}}
    <ul>
    {{#each orders}}
      <li>Order {{this.id}}: ${{this.total}}</li>
    {{/each}}
    </ul>
  {{/if}}
`;

const data = {
  user: { firstName: 'Jane', lastName: 'Doe' },
  hasOrders: true,
  orders: [
    { id: 'A1', total: 99.99 },
    { id: 'A2', total: 149.99 }
  ]
};

const result = validateTemplate(handlebarsTemplate, data, TemplateEngine.HANDLEBARS);
console.log('Valid:', result.isValid, 'Score:', result.score);

Custom Variable Patterns

import { validateTemplate, TemplateEngine } from '@bernierllc/template-validator';

const template = 'Hello %name%, welcome to %site%!';
const data = { name: 'Alice', site: 'Portal' };

const customPattern = {
  engine: TemplateEngine.AUTO,
  pattern: /%([a-zA-Z_][a-zA-Z0-9_]*)%/g,
  extractName: (match: string) => match.replace(/%/g, '')
};

const result = validateTemplate(template, data, TemplateEngine.AUTO, {
  customPatterns: [customPattern]
});

console.log('Unreplaced vars:', result.unreplacedVariables.length); // 0

Syntax Validation Only

import { validateTemplateSyntax, TemplateEngine } from '@bernierllc/template-validator';

const template = `
  {% if user %}
    {{ user.name }}
  {% endif %}
  {% for item in items %}
    {{ item }}
  {% endfor %}
`;

const result = validateTemplateSyntax(template, TemplateEngine.LIQUID);

if (!result.isValid) {
  console.error('Syntax errors:');
  result.errors.forEach(err => {
    console.error(`  ${err.message} at line ${err.location?.line}`);
  });
}

if (result.warnings.length > 0) {
  console.warn('Warnings:');
  result.warnings.forEach(warn => {
    console.warn(`  ${warn.message}`);
  });
}

Score Calculation

The validation score (0-100) is calculated as follows:

  • -20 points per syntax error (major issues)
  • -15 points per missing required variable
  • -10 points per unreplaced variable
  • -8 points per conditional issue
  • -5 points per missing optional variable

A score of 100 indicates a perfect template with no issues.

TypeScript Types

interface UnreplacedVariable {
  variable: string;
  pattern: string;
  location: TextLocation;
  expectedValue?: unknown;
}

interface MissingVariable {
  name: string;
  required: boolean;
  type: VariableType;
  location?: TextLocation;
}

interface TemplateSyntaxError {
  type: 'syntax';
  message: string;
  location?: TextLocation;
  engine: TemplateEngine;
}

interface VariableRequirement {
  name: string;
  type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'any';
  required: boolean;
  defaultValue?: unknown;
}

Integration Status

  • Logger Integration: Not applicable - Pure validation utility with no logging requirements. This is an atomic core package that performs deterministic validation without side effects.
  • Docs-Suite: Ready - Markdown documentation with TypeScript examples
  • NeverHub Integration: Not applicable - Core atomic utility package with no event publishing or service discovery requirements. This package provides pure validation functions that can be used by higher-level packages that integrate with NeverHub.

Dependencies

  • @bernierllc/template-engine - Template engine type definitions

Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Build
npm run build

# Lint
npm run lint

Testing

This package achieves 90%+ test coverage with comprehensive tests covering:

  • Variable detection across all supported engines
  • Syntax validation for each template engine
  • Type validation for all variable types
  • Real-world email and document templates
  • Edge cases and error conditions

See Also

License

Copyright (c) 2025 Bernier LLC. All rights reserved.

This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.