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

esbuild-ejs

v0.1.0

Published

esbuild plugin to compile .ejs templates into JS modules

Readme

esbuild-ejs

An esbuild plugin that compiles .ejs templates into JavaScript modules. Each template exports a function that returns an array of DOM elements built with dolla's createElement.

Installation

npm install esbuild-ejs

Requires esbuild as a peer dependency.

Usage

Build configuration

import esbuild from 'esbuild';
import ejsPlugin from 'esbuild-ejs';

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'out.js',
  plugins: [ejsPlugin()],
});

Importing templates

import layout from './layouts/application.html.ejs';

const elements = layout();
for (const el of elements) {
  document.body.appendChild(el);
}

Template syntax

A template like this:

<div><span><%= 'Hello World' %></span></div>

Compiles to:

import createElement from 'dolla/createElement';

export default function application() {
  return [createElement("div", {content: createElement("span", {content: 'Hello World'})})];
}

Tags

| Tag | Purpose | |-----|---------| | <%= expr %> | Evaluate expression and output the result | | <%- expr %> | Evaluate expression and output without escaping | | <% code %> | Execute JavaScript without outputting | | <%# comment %> | Comment (omitted from output) |

Features

Parameter extraction

Free variables used in template expressions are automatically extracted into destructured function parameters. Locally declared variables, function params, arrow params, and JS builtins are excluded.

<div><%= user.name %></div>
<div><%= avatarTemplate({ account: account }) %></div>

Compiles to:

export default function template({user, avatarTemplate, account}) {
  return [createElement("div", {content: user.name}), createElement("div", {content: avatarTemplate({ account: account })})];
}

Subtemplates

Define reusable functions within templates. Function and arrow subtemplates return their content and can be called with <%= %>.

<% function renderItem(x) { %>
  <span><%= x %></span>
<% } %>
<div><%= renderItem(name) %></div>

Callback subtemplates work with iterators and higher-order functions:

<% formTag(function () { %>
  <input type="text">
  <input type="submit" />
<% }) %>

Iterators

Use forEach or map to loop over data:

<ul>
<% items.forEach((item) => { %>
  <li><%= item %></li>
<% }) %>
</ul>

map with <%= %> inlines the results:

<table>
<%= rows.map(row => { %>
  <tr><%= row %></tr>
<% }) %>
</table>

Conditional statements

<% if (show) { %>
  <div class="visible">yes</div>
<% } else { %>
  <div class="hidden">no</div>
<% } %>

Attribute interpolation

Embed expressions inside HTML attributes:

<div class="uniformLabel [[= foo ? 'disabled' : 'bold' ]] -yellow">
  Hello World
</div>

Comment scrubbing

Both EJS comments and HTML comments are stripped from output:

<%# This EJS comment is removed %>
<!-- This HTML comment is also removed -->

Import hoisting

Import statements in <% %> tags are hoisted to the module level:

<% import listenerElement from 'dolla/listenerElement' %>
<%= listenerElement('div', {content: 'click me'}, 'click', handler) %>

Compiles to:

import createElement from 'dolla/createElement';
import listenerElement from 'dolla/listenerElement';

export default function template({handler}) {
  return [listenerElement('div', {content: 'click me'}, 'click', handler)];
}

If a template imports createElement from another package, the built-in dolla import is automatically aliased to __createElement to avoid collisions.

Promise rendering

Template expressions can contain Promises. The promise passes through in the result array for the caller to await:

<div><%= fetchData() %></div>
const result = template({fetchData: () => fetch('/api').then(r => 'loaded')});
document.body.append(result)

starts as

<body><div></div></body>

then when loaded

Options

ejsPlugin({
  filter: /\.ejs$/i,  // regex to match files (default: /\.ejs$/i)
  open: '<%',         // opening delimiter (default: '<%')
  close: '%>',        // closing delimiter (default: '%>')
})

Custom delimiters

ejsPlugin({ open: '[[', close: ']]' })

Then in your template:

<div><span>[[= 'Hello World' ]]</span></div>

Development

git clone https://github.com/laserkats/esbuild-ejs.git
cd esbuild-ejs
npm install

Running tests

npm test

Project structure

src/
  index.js                   - esbuild plugin entry point
  template.js                - state-machine parser and code generator
  template/
    balance-scanner.js       - bracket/brace balance tracking
    html-tag.js              - HTML element code generation
    js.js                    - JavaScript expression node
    string.js                - text content node
    subtemplate.js            - block construct (loops, conditionals, callbacks)
    var-generator.js         - unique variable name generator
test/
  compilation_test.js        - compiled output assertions
  dom_test.js                - runtime DOM assertions via linkedom
  integration_test.js        - esbuild plugin integration tests
  setup.js                   - linkedom DOM globals setup
  fixtures/                  - template fixtures used by tests

License

MIT