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 🙏

© 2026 – Pkg Stats / Ryan Hefner

lazor

v1.0.0

Published

A razor-inspired javascript view engine

Readme

Lazor

Lazor is yet another view engine for Javascript. It's inspired by ASP.NET's Razor view engine and has similar semantics. It's written in LiveScript and intended for use on the server-side with Node.

DISCLAIMER: I made this as a learning exercise. As such, Lazor is beta quality and may not be suitable for production code. Use at your own risk.

Getting Started

Take a look at this simple simple example:

// in app.js
var view = require('./src/lazor')('./example/views', './example/temp');

var model = {
	terms: 'about & stuff',
	searchResults: [
		{ url: '/about', name: 'About Us', summary: '<p>The <em>about</em> us page!</p>' },
		{ url: '/', name: 'Home Page', summary: '<p>Find out <em>about</em> our homepage!</p>' }
	]
};

var result = view('~/sample.lz.html', model);

console.log(result);
<!-- in sample.lz.html -->
@{
	layout = "~/sample.template.lz.html";
	viewState.title = "Search Results";
}

@section welcome {
	<h1>Search Results</h1>
}

<p>Your search for for <a href="/search/?q=@uri(model.terms)">@model.terms</a> resulted in @(model.searchResults.length) results:</p>

<div class="results">
	@for (result in model.searchResults) {
		partial('~/result.lz.html', result);
	}
</div>
<!-- in result.lz.html -->
<div class="result">
	<h2><a href="@model.url">@model.name</a></h2>
	<div class="summary">@raw(model.summary)</div>
</div>
<!-- In sample.template.lz.html -->
<!DOCTYPE html>
<html>
	<head>
		<title>@(viewState.pageName || 'Welcome!')</title>
	</head>
	<body>

	@render('welcome')

	@body()

	@render('footer', () -> <div class="footer">&copy; 2015</div>)

	</body>
</html>

Why Would I Use This?

While this was written primarily as an academic exercise, it does offer a few advantages to other Razor-inspired view engines. Namely:

  • It has a full parser (instead of a regex-based parser), and is not confused by things like: @raw("Hi :)")
  • It HTML-encodes everything by default. You have to explicitly declare that something should not be HTML-encoded (by wrapping in a raw() call).
  • Supports a very razor-like syntax for using layouts, helpers, and sections.
  • Templates are compiled into javascript and cached. A template is only ever parsed once during the lifetime of an application.

There are Caveats

  • Lazor is sometimes very "un-node", for example, during the transpile phrase, it reads templates and writes transpiled javascript synchronously. This may be remedied in the future.
  • Lazor does some not-so-effecient things, such as loading templates entirely into memory, buffering all output into memory, lots of string concatination.
  • The HTML parser is far stricter than a normal HTML 5 parser. Your HTML must be very well formed.
  • Not everything supported by javascript is supported by the scripting language parser.
  • The view engine does not pick up changes made to templates in real time (the application must be restarted).
  • This was written by one guy in his free time, and may not be -- most certainly is not -- bug free.

How it Works

Lazor has it's own language and parser. The language itself is contextual; it has an HTML context and a scripting context. When a view is processed, Lazor reads the template, parses it to an abstract syntax tree, transpiles that tree into javascript, writes the javascript to a temp directory, and then requires it as a node module. Subsequent requests for that template use the cached javascript.

HTML Context

Inside an HTML context, tags and normal blocks of text are recognized. Every Lazor template begins in an HTML context.

Tags are actually parsed and stored in a dom-like structure, so your HTML must be well-formed. The Lazor parser is quite a bit stricter than an HTML 5 parser.

<!-- This is fine -->
<head lang="en">
  <title>Example</title>
  <link rel="stylesheet" href="/styles/core.css" />
</head>

<!-- This will fail. -->
<head lang=en> <!-- Attributes must be quoted -->
  <title>Example</title>
  <link rel="stylesheet" href="/styles/core.css"> <!-- Self-closing tags must be explicity closed -->
</head>

You switch from an HTML context to a scripting context with an @ symbol. You escape an @ symbol with another @ symbol. For example:

<p>@username, please email info@@example.com for help</p>

The first @ enters a scripting context and outputs a username variable, while the email address gets rendered as [email protected].

From an HTML context, simple expressions can be embedded directly:

<p>Hello @username, your city is: @data["city_name"](username).</p>
<p>Have a nice day. @raw("<b>:)</b>")!</p>

More complex expressions must be wrapped in parentheses:

<p>There are @(7 - today.dayOfWeek) days left in this week.</p>

Blocks of code are entered with curly braces.

@{
	var title = 'Hello';
}

All output from scripting context is HTML-encoded unless it is wrapped in a raw function call.

HTML comments are stripped from the page output.

Scripting Context

Lazor's scripting language is a JavaScript-like syntax, but is not JavaScript. There are a few major differences (some of which may change in subsequent releases):

  • There is no new keyword. Lazor is built around simple literals, including strings, integers, arrays, and objects. There is no concept of prototyping.
  • There is no function keyword. In Lazor, all functions are declared as variables using a lambda syntax. For example: var wrapH1 = (name) -> <h1>@name</h1>; declares a function called wrapH1 that accepts a name variable.
  • There is no return keyword.
  • Separators are technically optional - though recommended. t = [3, 4, 5]; t.push(6); and t = [3 4 5] t.push(6) parse to the exact same thing.
  • The conditional operator is not support (condition ? result1 : result2) -- this may be supported in the future.
  • Parsing floating point numbers is not yet support - This is on the roadmap.
  • No support for comments yet - This is also on the roadmap.

For loops are slightly different:

  • There is no for (var x = 0; x < source.length; x++) { } form.
  • You can loop through an array: for(link in navigation) { ouptputNavLink(link); }
  • You can loop through an object: for(key of keyValuePairs) { <div>@key = @keyValuePairs[key]</div> }
  • For all other loops, use while

You jump back into an HTML context simply by embedding a tag. If you want to embed some text without using a tag, use a text tag, which will put you in HTML context without actually outputing a container tag. For example:

@for(day in days) {
  day = day.toUpperCase();

  <text> | HAPPY @day!</text>
}

Roadmap

Things to fix or add, in no particular order:

  • Suport for parsing floats
  • Support for some kind of simple string interpolation
  • Support for recognizing common self-closing tags, and ignoring the fact that they are not explicitly self-closed.
  • A for loop syntax for looping through a range
  • Comments support inside a scripting context.
  • Async support
  • Add ability for view engine to detect template changes and automatically recompile
  • Write tests for the transpiler
  • Possibly support for something like _ViewStart.cshtml from Razor for setting global defaults.