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

@opuu/epml-escpos

v2.1.1

Published

An XML-style templating language compiler for ESC/POS thermal printers. Transform markup templates and JSON data into raw byte arrays.

Readme

EPML ESC/POS Markup Language

@opuu/epml-escpos is an XML-like template compiler for thermal receipt printers. It transforms EPML templates plus JSON data into raw ESC/POS bytes (Uint8Array).

Features

  • XML-style receipt templates
  • Variable interpolation with dot-paths ({{ order.total }})
  • Control flow: <for>, <if>, <else/>
  • Universal text styling attributes (bold, underline, invert, size, align, etc.)
  • Layout helpers: <row>, <cell>, <hr/>, <feed/>
  • Barcode support: 1D, QR, PDF417
  • Async image rendering from file path, URL, or data URL
  • Printer profiles (Standard Epson, Star Micronics ESC/POS mode)
  • Plugin system for overriding command bytes per printer family
  • Structured warnings and typed error classes

Installation

npm install @opuu/epml-escpos

Quick Start (Synchronous)

Use compile() when your template does not contain <image>.

import { EPMLCompiler } from "@opuu/epml-escpos";

const template = `
<receipt width="48" init="true">
  <text align="center" bold size="2">MY STORE</text>
  <hr/>
  <row>
    <cell width="70%">Coffee</cell>
    <cell width="30%" align="right">$3.50</cell>
  </row>
  <row>
    <cell width="70%" bold>TOTAL</cell>
    <cell width="30%" align="right" bold>$3.50</cell>
  </row>
  <feed lines="2"/>
  <cut mode="partial"/>
</receipt>
`;

const data = {};
const result = EPMLCompiler.compile(template, data);

// Raw ESC/POS bytes
const bytes: Uint8Array = result.bytes;

// Non-fatal warnings (e.g. unsupported capability in selected profile)
console.log(result.warnings);

Quick Start (Asynchronous + Images)

<image> requires compileAsync().

import { EPMLCompiler } from "@opuu/epml-escpos";

const template = `
<receipt width="48" init="true">
  <text align="center">Logo</text>
  <image width="200" dither="floyd-steinberg">https://example.com/logo.png</image>
  <feed lines="2"/>
  <cut mode="full"/>
</receipt>
`;

const result = await EPMLCompiler.compileAsync(template, {});
const bytes = result.bytes;

Image source can be:

  • local file path
  • http:// or https:// URL
  • data: URL

Sending Bytes to a Network Printer

import net from "node:net";
import { EPMLCompiler } from "@opuu/epml-escpos";

const { bytes } = EPMLCompiler.compile(`<text>Hello printer</text>`, {});

const socket = new net.Socket();
socket.connect(9100, "192.168.1.50", () => {
  socket.write(Buffer.from(bytes), () => socket.destroy());
});

Chunked Sending

Some printers, including many cheap USB, serial, and Bluetooth models have small receive buffers. Sending the full byte stream at once can overflow the buffer and produce a truncated receipt. Use chunks() to send in fixed-size pieces.

const result = EPMLCompiler.compile(template, data, profile, { chunkSize: 512 });

for (const chunk of result.chunks()) {
  await sendToPrinter(chunk);
}

You can also pass a size directly to chunks() to override the compile-time default:

for (const chunk of result.chunks(256)) {
  await sendToPrinter(chunk);
}

Template Language

Data Interpolation

Use {{ path }} where path is dot notation:

  • {{ customer.name }}
  • {{ items.0.price }}

Missing values render as an empty string.

Control Flow

<for item="line" in="cart.items">
  <row>
    <cell width="70%">{{ line.name }}</cell>
    <cell width="30%" align="right">{{ line.price }}</cell>
  </row>
</for>

<if condition="customer.isMember">
  <text bold>MEMBER PRICE APPLIED</text>
  <else/>
  <text>Sign up for rewards next time.</text>
</if>

Supported Tags

  • <receipt>
  • <text>
  • <br/>
  • <hr/>
  • <row>
  • <cell> (alias: <col>)
  • <for>
  • <if> + <else/>
  • <feed/>
  • <feed-dots/>
  • <feed-reverse/> (profile-dependent)
  • <drawer/> (alias: <open-drawer>)
  • <cut/>
  • <barcode>
  • <qr>
  • <pdf417>
  • <image> (async only)
  • <nv-image>

HTML comments are supported and ignored by the lexer (<!-- comment -->).

Universal Text Attributes

These are accepted on text-capable tags such as <text>, <cell>, <row>, and <receipt>.

| Attribute | Type | Default | Notes | | ---------------- | ------------------------- | ------- | ------------------------------------------- | | align | left \| center \| right | left | Text alignment | | bold | boolean | false | | | underline | boolean | false | | | strike | boolean | false | | | invert | boolean | false | White-on-black mode | | rotate | boolean | false | | | upside-down | boolean | false | | | color | black \| red | black | Profile-dependent | | font | a \| b | a | | | size | number | 1 | Uniform X/Y scale | | size-x | number | 1 | Horizontal scale override | | size-y | number | 1 | Vertical scale override | | charset | string | - | Must be supported by profile | | smoothing | boolean | false | Profile-dependent | | padding | boolean | false | Fill remaining width with padding-char | | full-width | boolean | false | Similar to padding behavior for text blocks | | inline | boolean | false | Prevent automatic trailing LF for <text> | | padding-char | string | space | First byte is used | | padding-top | number | 0 | Adds filled blank lines before content | | padding-bottom | number | 0 | Adds filled blank lines after content |

Tag-Specific Attributes

  • <receipt width="48" init="true">
  • <cell width="50%"> or <cell width="24">
  • <feed lines="1"/>
  • <feed-dots n="24"/>
  • <feed-reverse lines="1"/>
  • <drawer pin="2|5" on="50" off="50"/>
  • <cut mode="full|partial" feed="0"/>
  • <barcode type="CODE128" hri="none|above|below|both" hri-font="a|b" height="50" width="3">...data...</barcode>
  • <qr size="3" error="L|M|Q|H">...data...</qr>
  • <pdf417 cols="0" rows="0" error="0" truncated="false">...data...</pdf417>
  • <image src="..." mode="raster|column" scale="1" dither="threshold|bayer|floyd-steinberg" threshold="128" width="384"/>
  • <nv-image n="1" mode="normal|double-width|double-height|quad"/>

Profiles

The default profile is StandardEpsonProfile.

import { EPMLCompiler, StarMicronicsProfile } from "@opuu/epml-escpos";

const result = EPMLCompiler.compile(template, data, StarMicronicsProfile);

Built-in exports:

  • StandardEpsonProfile
  • StarMicronicsProfile

Plugins

Plugins override selected parts of the active profile command map.

import { EPMLCompiler, type EPMLPlugin } from "@opuu/epml-escpos";

const plugin: EPMLPlugin = {
  name: "my-printer-overrides",
  version: 1,
  commands: {
    text: {
      boldOn: new Uint8Array([0x1b, 0x45, 0x01]),
    },
  },
};

EPMLCompiler.use(plugin); // global registration

const result = EPMLCompiler.compile("<text bold>Hello</text>", {});

EPMLCompiler.unuse("my-printer-overrides");

You can also pass local plugins per compile call:

EPMLCompiler.compile(template, data, undefined, {
  plugins: [plugin],
});

API Overview

EPMLCompiler.compile(template, data, profile?, options?)

  • Synchronous compilation
  • Returns CompileResult
  • Throws on syntax/semantic/codegen/plugin errors
  • Cannot process <image> tags

EPMLCompiler.compileAsync(template, data, imageRendererOrProfile?, profileOrOptions?, maybeOptions?)

  • Asynchronous compilation
  • Supports <image> tags
  • Uses built-in rasterizer if you do not provide one

CompileResult

interface CompileResult {
  bytes: Uint8Array;
  warnings: EPMLWarning[];
  chunks(size?: number): Generator<Uint8Array>;
}

chunks() yields zero-copy Uint8Array views of bytes. The optional size overrides the chunkSize set in CompileOptions. When neither is set, a single chunk with the full buffer is returned.

Errors and Warnings

Error classes:

  • EPMLError
  • EPMLSyntaxError
  • EPMLSemanticError
  • EPMLCodegenError
  • EPMLPluginError

Warnings are returned in CompileResult.warnings and include source stage and optional line/column metadata.

Deprecated Tags

The following legacy tags are removed in favor of universal text attributes:

  • <b>, <u>, <strike>
  • <center>, <left>, <right>
  • <color>, <font>
  • <rotate>, <upside-down>
  • <i_text>
  • <line-spacing>, <reset-spacing>

Use <text ...attributes...> instead.

Development

npm run build
npm test

License

MIT