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

wasm-latex-tools

v0.1.1

Published

Run LaTeX tools (texcount, latexdiff, latexpand, tex-fmt) in the browser using WebAssembly

Readme

WASM LaTeX Tools

Run LaTeX tools (texcount, latexdiff, latexpand, tex-fmt) directly in your browser using WebAssembly. This package bundles WebPerl WASM for Perl-based tools and Rust WASM (wasm-bindgen) for tex-fmt, providing a complete LaTeX toolchain without server-side dependencies.

Features

  • TexCount: Count words in LaTeX documents with support for multi-file projects
  • LaTeX Diff: Generate diff documents showing changes between two LaTeX files
  • Latexpand: Expand LaTeX documents by inlining included files
  • TeX-Fmt: Format and beautify LaTeX documents using Rust WASM (wasm-bindgen)

All tools run entirely in the browser with no server required.

Installation

npm install wasm-latex-tools

Setup

Copy the required WASM and Perl assets to your public directory:

npx wasm-latex-tools copy-assets

This copies WebAssembly files and Perl scripts to ./public/core/ by default.

For a custom location:

npx wasm-latex-tools copy-assets ./static/wasm

Usage

Basic Example

import { WebPerlRunner, TexCount, TexFmt } from 'wasm-latex-tools';

// Initialize the WebPerl runner
const runner = new WebPerlRunner();
await runner.initialize();

// Count words in a LaTeX document
const texCount = new TexCount(runner);
const countResult = await texCount.count({
  input: '\\documentclass{article}\\n\\begin{document}\\nHello World\\n\\end{document}'
});

console.log(texCount.parseOutput(countResult.output));

// Format a LaTeX document with tex-fmt (wasm-bindgen)
const texFmt = new TexFmt();
const formatResult = await texFmt.format({
  input: '\\documentclass{article}\\n\\begin{document}\\nHello World\\n\\end{document}',
  wrap: true,
  wraplen: 80
});

console.log(formatResult.output);

Configuration

If you copied assets to a custom location, configure the paths:

const runner = new WebPerlRunner({
  webperlBasePath: '/wasm/webperl',
  perlScriptsPath: '/wasm/perl'
});

TeXcount with Multi-File Support

const texCount = new TexCount(runner);

const result = await texCount.count({
  input: mainFileContent,
  includeFiles: true,
  merge: false,
  additionalFiles: [
    { path: 'chapter1.tex', content: chapter1Content },
    { path: 'chapter2.tex', content: chapter2Content }
  ]
});

const parsed = texCount.parseOutput(result.output);
console.log(`Words: ${parsed.words}, Headers: ${parsed.headers}`);

Latexdiff

import { LatexDiff } from 'wasm-latex-tools';

const latexDiff = new LatexDiff(runner);

const result = await latexDiff.diff(oldContent, newContent, {
  type: 'UNDERLINE',
  flatten: true
});

console.log(result.output); // Diff LaTeX document

Latexpand

import { Latexpand } from 'wasm-latex-tools';

const latexpand = new Latexpand(runner);

const result = await latexpand.expand({
  input: mainFileContent,
  keepComments: true,
  additionalFiles: [
    { path: 'intro.tex', content: introContent },
    { path: 'conclusion.tex', content: conclusionContent }
  ]
});

console.log(result.output); // Expanded LaTeX document

TeX-Fmt Options

const texFmt = new TexFmt();

const result = await texFmt.format({
  input: latexContent,
  wrap: true,
  wraplen: 100,
  tabsize: 4,
  usetabs: false
});

API Reference

WebPerlRunner

The core runner for Perl-based tools.

const runner = new WebPerlRunner({
  webperlBasePath?: string;  // Default: '/core/webperl'
  perlScriptsPath?: string;  // Default: '/core/perl'
  verbose?: boolean;         // Default: false
});

await runner.initialize();

TeXcount

Count words in LaTeX documents.

const texCount = new TexCount(runner, verbose?);

const result = await texCount.count({
  input: string;
  brief?: boolean;
  total?: boolean;
  sum?: boolean;
  verbose?: number;
  includeFiles?: boolean;
  merge?: boolean;
  additionalFiles?: { path: string; content: string }[];
});

const parsed = texCount.parseOutput(result.output);
// Returns: { words: number, headers: number, captions: number, raw: string }

Latexdiff

Generate diff documents between two LaTeX files.

const latexDiff = new LatexDiff(runner, verbose?);

const result = await latexDiff.diff(oldContent, newContent, {
  type?: 'UNDERLINE' | 'CTRADITIONAL' | 'CFONT' | 'CHANGEBAR';
  subtype?: string;
  floattype?: 'FLOATSAFE' | 'IDENTICAL';
  encoding?: string;
  mathMarkup?: number;
  allowSpaces?: boolean;
  flatten?: boolean;
});

Latexpand

Expand LaTeX documents by inlining includes.

const latexpand = new Latexpand(runner, verbose?);

const result = await latexpand.expand({
  input: string;
  keepComments?: boolean;
  emptyComments?: boolean;
  expandUsepackage?: boolean;
  makeatletter?: boolean;
  showGraphics?: boolean;
  fatal?: boolean;
  additionalFiles?: { path: string; content: string }[];
});

TeX-Fmt

Format LaTeX documents with Rust WASM (wasm-bindgen), with no runner needed.

const texFmt = new TexFmt(verbose?, wasmBasePath?);

const result = await texFmt.format({
  input: string;
  wrap?: boolean;
  wraplen?: number;
  tabsize?: number;
  usetabs?: boolean;
});

Framework Integration

Next.js

npx wasm-latex-tools copy-assets ./public/core
// pages/index.tsx or app/page.tsx
'use client'; // For App Router

import { WebPerlRunner, TexCount } from 'wasm-latex-tools';
import { useEffect, useState } from 'react';

export default function Page() {
  const [runner, setRunner] = useState<WebPerlRunner | null>(null);

  useEffect(() => {
    const initRunner = async () => {
      const r = new WebPerlRunner();
      await r.initialize();
      setRunner(r);
    };
    initRunner();
  }, []);

  // Use runner...
}

Vite

npx wasm-latex-tools copy-assets ./public/core
import { WebPerlRunner, TexCount } from 'wasm-latex-tools';

const runner = new WebPerlRunner();
await runner.initialize();

Create React App

npx wasm-latex-tools copy-assets ./public/core

Same usage as Vite example above.

Custom Setup

For custom static file servers, copy assets to your static directory and configure paths:

npx wasm-latex-tools copy-assets ./static/latex-tools
const runner = new WebPerlRunner({
  webperlBasePath: '/latex-tools/webperl',
  perlScriptsPath: '/latex-tools/perl'
});

Building from Source

git clone https://github.com/TeXlyre/wasm-latex-tools.git
cd wasm-latex-tools
npm install
npm run build

Examples

Running the Demo

To run the interactive demo locally:

npm install
npm run build
npm run example

Then open http://localhost:3000 in your browser.

GitHub Pages Demo

To run the GitHub Pages example:

npm run build:pages-example
npm run pages-example

Asset Management

Copy Command

# Default location (./public/core)
npx wasm-latex-tools copy-assets

# Custom location
npx wasm-latex-tools copy-assets ./static/wasm

# In package.json scripts
{
  "scripts": {
    "postinstall": "wasm-latex-tools copy-assets"
  }
}

Asset Structure

After running copy-assets, your directory will contain:

public/core/
├── webperl/
│   ├── emperl.js
│   ├── emperl.wasm
│   ├── perlrunner.html
│   └── webperl.js
├── perl/
│   ├── texcount.pl
│   ├── latexdiff.pl
│   └── latexpand.pl
└── texfmt/
    ├── tex_fmt.js
    └── tex_fmt_bg.wasm

Performance Considerations

  • First Load: WebPerl WASM initialization takes 1-2 seconds
  • Subsequent Runs: Tool execution is fast (milliseconds for small documents)
  • Large Documents: Multi-file projects with hundreds of pages process in seconds
  • Memory: Each tool creates temporary files in WASM filesystem

Troubleshooting

Assets Not Found (404)

Ensure you've run the copy command and configured paths correctly:

npx wasm-latex-tools copy-assets

CORS Errors

Make sure your development server serves the assets directory. Most frameworks handle this automatically for the public/ directory.

Timeout Errors

For very large documents, you may need to increase timeouts. The default is 60 seconds for script execution.

Memory Issues

WebPerl runs in WASM with limited memory. For very large projects, consider splitting into smaller chunks.

Acknowledgments

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

AGPL-3.0 License © 2025 Fares Abawi