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 })
Build succeeds. Runtime error:
Uncaught ReferenceError: fs is not defined
at Object.readSourceMap (terser/dist/bundle.min.js:1:xxxxx)
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')
}
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()
}
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
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
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()
}
Library Selection Checklist
Before choosing a library, run through this:
-
Does
package.jsonhave abrowserfield? — Yes → browser-adapted -
Does the dependency tree include
fs,path,net? — Yes → can't use in browser - Does the npm page mention "Browser Support"?
- Search GitHub Issues for "browser", "client-side" — Any similar reports?
- 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)