DEV Community

Mark
Mark

Posted on

Building Pure Frontend Formatter Tools: Why Node.js Libraries Fail in the Browser

Building a pure-frontend online formatter tool site seems straightforward: import a formatting library → call it in the browser.
In practice, three commonly-used libraries all failed in the browser.

The Problem List

During development, three popular libraries all broke in the browser:

Library Purpose Browser Behavior
terser JS minification fs is not defined
html-minifier-terser HTML minification fs is not defined
xml-formatter v3.7.0 XML formatting ⚠️ ESM/CJS interop issues

terser: The fs Module Problem

Reproduction

import { minify } from 'terser'

// Called in a browser component
const result = await minify(code, { compress: true, mangle: true })
Enter fullscreen mode Exit fullscreen mode

Build succeeds. Runtime error:

Uncaught ReferenceError: fs is not defined
    at Object.readSourceMap (terser/dist/bundle.min.js:1:xxxxx)
Enter fullscreen mode Exit fullscreen mode

Root Cause

terser internally calls fs.readFileSync to read source map files. Fine in Node.js — fs doesn't exist in browsers.

Even though terser's package.json declares a browser field for conditional exports, some internal modules still reference fs, and webpack/SWC tree-shaking doesn't fully eliminate them.

Solution: Lightweight Regex Minifier

function minifyJs(code) {
  return code
    // Remove single-line comments
    .replace(/\/\/.*$/gm, '')
    // Remove multi-line comments
    .replace(/\/\*[\s\S]*?\*\//g, '')
    // Collapse whitespace
    .replace(/\s+/g, ' ')
    .trim()
    // Remove spaces around punctuation
    .replace(/\s*([,;{}()\[\]])\s*/g, '$1')
    // Remove blank lines
    .replace(/\n\s*\n/g, '\n')
}
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Zero dependencies, pure JS
  • Works in both browser and Node.js
  • Tiny codebase (20 lines)
  • More than sufficient for a tool site's JS minification

Disadvantages:

  • Lower compression ratio than Terser
  • No AST-level optimizations (variable mangling, dead code elimination)

But for "user pastes code in browser to see minified output," regex is more than enough.

html-minifier-terser: Same fs Problem

Problem

Same root cause as terser. html-minifier-terser calls fs internally to read local files for <script src="..."> etc.

Solution

function minifyHtml(html) {
  return html
    .replace(/<!--[\s\S]*?-->/g, '')   // Remove HTML comments
    .replace(/>\s+</g, '><')            // Collapse whitespace between tags
    .replace(/\s*=\s*/g, '=')           // Tighten attribute values
    .replace(/ {2,}/g, ' ')             // Collapse multiple spaces
    .trim()
}
Enter fullscreen mode Exit fullscreen mode

xml-formatter: ESM/CJS Interop Issues

Problem

This library actually supports the browser. But v3.7.0's export structure is messy:

// xml-formatter v3.7.0 actual exports
export default function formatXml(...) { ... }  // default export
export function minify(xml) { ... }             // named export
Enter fullscreen mode Exit fullscreen mode

After SWC compilation (CJS wrapper), the default export becomes exports.default. Direct import formatXml from 'xml-formatter' sometimes returns a non-function object.

Debugging

// ❌ formatXml is undefined at runtime
import formatXml from 'xml-formatter'
import { minify } from 'xml-formatter'  // ✅ This works

// ✅ Correct approach
import _formatXml from 'xml-formatter'
const formatXml = _formatXml.default || _formatXml
Enter fullscreen mode Exit fullscreen mode

Final Solution

// Format with xml-formatter (interop handled)
import _formatXml from 'xml-formatter'
const formatXml = _formatXml.default || _formatXml

// Minify with custom function (xml-formatter's minify is inconsistent across versions)
function minifyXml(xml) {
  return xml
    .replace(/<!--[\s\S]*?-->/g, '')
    .replace(/\s+/g, ' ')
    .replace(/>\s+</g, '><')
    .trim()
}
Enter fullscreen mode Exit fullscreen mode

Library Selection Checklist

Before choosing a library, run through this:

  1. Does package.json have a browser field? — Yes → browser-adapted
  2. Does the dependency tree include fs, path, net? — Yes → can't use in browser
  3. Does the npm page mention "Browser Support"?
  4. Search GitHub Issues for "browser", "client-side" — Any similar reports?
  5. Test locally with a simple HTML + <script type="module">

Recommended Libraries for Pure Frontend Tools

Function Library Why
JS/HTML/XML formatting js-beautify Explicit browser build support
CSS minification csso Pure JS, no Node.js deps
JSON formatting JSON.stringify/parse Native API
SQL formatting sql-formatter Browser-compatible
YAML formatting js-yaml Browser-compatible

Core Lesson

Don't judge a library by its name. Judge it by its runtime dependencies.
"js" in the name doesn't mean it runs in browsers. "node" in the name doesn't mean it can't.

Project

utlkit.com — All formatter tools run purely in the browser. The solutions above are running stably in production.

Top comments (0)