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

markup-generator

v3.2.0

Published

TypeScript helper to generate unique filenames and write HTML (or any text) content to disk.

Readme

markup-generator

A small, dependency-light module for writing generated content to files — handles directory creation, UTF-8 defaults, stable/unique filenames, and typed errors, so consuming projects don't have to hand-roll mkdirSync/writeFileSync logic.

Written on TypeScript, can generate a uniquie filenames.

Arthur calling it WRITE because one of the main goals for this project was to write a file on a disk

Installation

npm install markup-generator

Requires Node 20+. Ships as a dual CJS/ESM build with bundled TypeScript types.

Why

Several sibling projects independently reinvented the same file-writing boilerplate — manual directory creation, raw fs calls, and ad-hoc error handling. This module centralizes that logic so every consumer gets the same guarantees: missing directories are created automatically, content is validated before writing, and failures come back as a single typed error instead of inconsistent thrown exceptions.

API

writeGeneratedFile({ content, fileName, dir })

Writes content to dir/fileName, creating dir if it doesn't exist. Defaults to UTF-8 encoding.

const { writeGeneratedFile } = require('markup-generator');

const outPath = await writeGeneratedFile({
  content: '<html>...</html>',
  fileName: 'newsletter.html',
  dir: 'generated',
});

Resolves with the absolute path written to. Throws MarkupGeneratorError on failure (see Error handling).

writeGeneratedEmail({ content, fileName, label, dir })

A higher-level wrapper around writeGeneratedFile, purpose-built for email-generation scripts. Adds friendly console logging and graceful failure handling.

const { writeGeneratedEmail } = require('markup-generator');

await writeGeneratedEmail({
  content: renderedHtml,
  fileName: 'hackernoon-email.html',
  label: 'Hackernoon',
});
  • On success: writes the file and logs a confirmation.
  • On MarkupGeneratorError: logs ❌ Failed to write <file> [<code>]: <message>, sets process.exitCode = 1, and resolves undefined — does not throw.
  • Any other error type still throws, so unexpected failures aren't silently swallowed.

generateFileName(prefix)

Generates a unique filename by appending a UUID to prefix.

const { generateFileName } = require('markup-generator');

generateFileName('hackernoon-email');
// → 'hackernoon-email-3f9c2a10-....html' (illustrative — check exact format in source)

⚠️ Because this appends a UUID, it produces a different filename on every call. Don't use it where a caller expects a stable, predictable filename — pass fileName explicitly to writeGeneratedFile/writeGeneratedEmail instead in that case.

readJson(path) / writeJson(path, data)

Typed JSON read/write helpers (src/json-io.ts).

const { readJson, writeJson } = require('markup-generator');

const config = await readJson('config.json');
await writeJson('output.json', { updated: true });

Throws MarkupGeneratorError with code JSON_READ (file couldn't be read) or JSON_PARSE (invalid JSON) on failure.

loadData(path)

Loads a data module — .json files are parsed via readJson; .js/.mjs/.cjs files are loaded via dynamic import (CJS-safe, so it works correctly even when bundled/transpiled to CommonJS). Resolved relative to process.cwd() unless the path is already absolute.

const { loadData } = require('markup-generator');

const data = await loadData('content/data.js');

loadContent(path)

Reads a plain content file (HTML, text, markdown) as a UTF-8 string. Resolved relative to process.cwd() unless already absolute.

const { loadContent } = require('markup-generator');

const html = loadContent('templates/fallback.html');

resolveFromCwd(path)

Resolves a path relative to process.cwd(), unless it's already absolute. Used internally by the read/write helpers above; exported for consumers who want the same resolution behavior in their own scripts.

const { resolveFromCwd } = require('markup-generator');

resolveFromCwd('output/file.html'); // → absolute path

generateTemplate({ render, argv, cwd, defaultTemplateId, defaultDataPath })

An end-to-end CLI helper for "render a template, write the result" scripts — parses CLI arguments, loads data/content, calls your render function, and writes the output, all in one call.

const { generateTemplate } = require('markup-generator');
const { renderTemplate } = require('./dist/index.cjs.js');

generateTemplate({
  render: renderTemplate,
  argv: process.argv,
  defaultDataPath: 'content/content2.js',
}).catch((err) => {
  console.error(err.message);
  process.exit(1);
});

This bundles CLI parsing, data loading, and rendering-orchestration into one call. If your script's argument format or fallback-content logic diverges from the built-in defaults, use the lower-level parseArgv/loadData/loadContent/writeGeneratedFile pieces directly instead.

Error handling

Every failure from this module is a MarkupGeneratorError — a single error class with a code field, so callers can branch on failure type without parsing message strings.

import { MarkupGeneratorError } from 'markup-generator';

try {
  await writeGeneratedFile({ content: '', fileName: 'out.html', dir: 'generated' });
} catch (error) {
  if (error instanceof MarkupGeneratorError) {
    console.error(`[${error.code}] ${error.message}`);
  } else {
    throw error;
  }
}

| Code | Meaning | |---|---| | EMPTY_CONTENT | content was an empty string | | NOT_A_STRING | content was not a string | | FILE_EXISTS | Target file already exists and wasn't expected to | | WRITE_FAILED | The underlying file write failed | | JSON_READ | A JSON file couldn't be read from disk | | JSON_PARSE | A file's contents were not valid JSON |

Development

npm install
npm test        # Vitest
npm run build   # tsup — outputs dual CJS/ESM + type declarations to dist/
npm run lint

Test runner: Vitest (migrated from Jest). Build tool: tsup.

Migration

See MIGRATION.md for breaking-change notes between major versions.

License

See LICENSE.