Skip to main content
Glama

validate_template

Check NoJS HTML templates for syntax errors, unknown directives, and adherence to best practices to ensure proper functionality.

Instructions

Validate a NoJS HTML template for syntax errors, unknown directives, and best practices

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
htmlYesThe HTML template to validate

Implementation Reference

  • The handler function that validates the NoJS template for syntax, common errors, and best practices.
    async ({ html }) => {
        const errors: string[] = [];
        const warnings: string[] = [];
        const knownDirectives = getDirectiveNames();
    
        // Extract all attributes from HTML
        const attrRegex = /\s([a-z][a-z0-9\-:]*?)(?:=|[\s>])/gi;
        const attrs = new Set<string>();
        let match: RegExpExecArray | null;
        while ((match = attrRegex.exec(html)) !== null) {
            attrs.add(match[1].toLowerCase());
        }
    
        // Check for potential NoJS directive typos
        const possibleTypos: Record<string, string> = {
            bnd: "bind",
            bing: "bind",
            binde: "bind",
            "bind-htm": "bind-html",
            iff: "if",
            els: "else",
            "else-iff": "else-if",
            shw: "show",
            hid: "hide",
            ech: "each",
            forech: "foreach",
            modle: "model",
            stat: "state",
            stor: "store",
            rout: "route",
            "route-vew": "route-view",
            validat: "validate",
            animat: "animate",
            "on-click": "on:click",
            "on-submit": "on:submit",
            "on-input": "on:input",
        };
    
        for (const attr of attrs) {
            if (possibleTypos[attr]) {
                errors.push(
                    `Unknown attribute "${attr}" — did you mean "${possibleTypos[attr]}"?`
                );
            }
        }
    
        // Check for each without "in" keyword
        const eachRegex = /each="([^"]+)"/g;
        while ((match = eachRegex.exec(html)) !== null) {
            if (!match[1].includes(" in ")) {
                errors.push(
                    `Invalid "each" syntax: "${match[1]}" — expected format: "item in items"`
                );
            }
        }
    
        // Check foreach without from
        if (html.includes("foreach=") && !html.includes("from=")) {
            errors.push(
                `"foreach" directive requires a "from" attribute to specify the source array`
            );
        }
    
        // Check for deprecated syntax
        if (html.includes('mode="hash"') || html.includes("mode=\"history\"")) {
            warnings.push(
                `Deprecated: router "mode" option. Use "useHash: true" instead of "mode: 'hash'"`
            );
        }
    
        // Check model on non-input elements
        const modelOnDivRegex =
            /<(div|span|p|h[1-6]|section|article|main|header|footer)\s[^>]*model=/gi;
        while ((match = modelOnDivRegex.exec(html)) !== null) {
            warnings.push(
                `"model" directive on <${match[1]}> — "model" is designed for form inputs (<input>, <select>, <textarea>)`
            );
        }
    
        // Check bind-html without sanitization warning
        if (html.includes("bind-html=")) {
            warnings.push(
                `"bind-html" renders raw HTML. Ensure the content is sanitized to prevent XSS.`
            );
        }
    
        // Check for on: events without correct syntax
        const onRegex = /on:([a-z]+)\.([a-z.]+)/gi;
        const validModifiers = new Set([
            "prevent",
            "stop",
            "once",
            "self",
            "enter",
            "escape",
            "tab",
            "space",
            "up",
            "down",
            "left",
            "right",
            "ctrl",
            "alt",
            "shift",
            "meta",
        ]);
        while ((match = onRegex.exec(html)) !== null) {
            const mods = match[2].split(".");
            for (const mod of mods) {
                if (!validModifiers.has(mod)) {
                    warnings.push(
                        `Unknown event modifier ".${mod}" on "on:${match[1]}" — valid modifiers: ${[...validModifiers].join(", ")}`
                    );
                }
            }
        }
    
        const valid = errors.length === 0;
    
        let summary = valid
            ? "✅ Template is valid."
            : `❌ Found ${errors.length} error(s).`;
        if (warnings.length > 0) {
            summary += ` ${warnings.length} warning(s).`;
        }
    
        return {
            content: [
                {
                    type: "text" as const,
                    text: JSON.stringify({ valid, errors, warnings, summary }, null, 2),
                },
            ],
        };
    }
  • Tool registration for validate_template in src/tools/index.ts
    server.tool(
        "validate_template",
        "Validate a NoJS HTML template for syntax errors, unknown directives, and best practices",
        { html: z.string().describe("The HTML template to validate") },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ErickXavier/nojs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server