mowdown: HTML, CSS, and JS Production Bundler
Mowdown is a lightweight, standalone JavaScript bundler designed for straightforward production preparation of web applications. Currently at version 2.2.2, it provides a minimal approach to consolidating and optimizing HTML, CSS, and JavaScript assets without requiring complex build configurations or a persistent local server process. The tool operates by scanning specified HTML files, identifying local CSS and JavaScript references (via `<link>` and `<script>` tags), and then concatenating, minifying, and optionally applying Babel transformations to these assets. Its primary differentiator is its simplicity and "no build step" philosophy, aiming to reduce developer overhead by working directly with existing HTML structures rather than introducing module systems or advanced optimizations like tree shaking or code splitting. It is suitable for projects where the build process needs to be minimal and predictable, especially for static sites or simple web applications. It operates purely as a Node.js utility.
Common errors
-
Error: Cannot find module '@babel/core' (or similar Babel-related error)
cause Mowdown relies on Babel for JavaScript processing, and a `.babelrc` file is missing or misconfigured in the project root.fixEnsure a `.babelrc` file exists in your project's root directory. Even a basic configuration like `{ "presets": ["@babel/preset-env"] }` is required. Verify that necessary Babel packages are available in mowdown's internal dependencies or your project's `node_modules` if you're running locally. -
Error: ENOENT: no such file or directory, stat 'path/to/my/asset.css'
cause Mowdown failed to locate a referenced CSS or JavaScript asset file at the path specified in your HTML relative to the HTML file or the `sourceFolder` option.fixDouble-check the paths specified in your HTML `<script src="...">` and `<link href="...">` tags. If your assets are not in the same directory as your HTML files, ensure the `sourceFolder` option is correctly pointing to the directory containing all your asset files. -
Original HTML files are modified/overwritten unexpectedly or bundled output doesn't match expectations.
cause The `isOverwritingHtml` option defaults to `true`, leading to source HTML files being directly modified or replaced if the `outputFolder` is not specified or is the same as the input HTML's directory.fixProvide a dedicated `outputFolder` that is different from your `sourceFolder` to receive the bundled output, or explicitly set `isOverwritingHtml: false` within the options object passed to the `mowdown` function to prevent source file modification.
Warnings
- gotcha Mowdown implicitly assumes all CSS files referenced in HTML are local. If you are using a CDN-hosted CSS file, you must explicitly add it to both the `excludeCss` and `prependCssUrls` options. Failure to do so can lead to build errors, incorrect paths, or missing styles in the bundled output.
- gotcha A `.babelrc` configuration file is required in the root directory for mowdown to correctly process JavaScript files, even if the `isUsingBabel` option is set to `false` or if you don't intend to use Babel transformations. Its absence will lead to errors during the bundling process.
- gotcha The `isOverwritingHtml` option defaults to `true`. This means if you do not specify a distinct `outputFolder` or if `outputFolder` is the same as your input HTML file's directory, mowdown will overwrite your original HTML files with the bundled versions. This can lead to accidental data loss.
Install
-
npm install mowdown -
yarn add mowdown -
pnpm add mowdown
Imports
- mowdown
import mowdown from 'mowdown'
const mowdown = require('mowdown') - mowdown (function call)
new mowdown(arrayOfHtmlFiles, outputFolder, options)
mowdown(arrayOfHtmlFiles, outputFolder, options)
Quickstart
const mowdown = require('mowdown');
const fs = require('fs');
const path = require('path');
// Create temporary input and output directories
const inputDir = path.join(__dirname, 'temp_mowdown_input');
const outputDir = path.join(__dirname, 'temp_mowdown_output');
fs.mkdirSync(inputDir, { recursive: true });
fs.mkdirSync(outputDir, { recursive: true });
// Create dummy HTML, CSS, JS files for bundling
const htmlContent = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mowdown Test</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Welcome to Mowdown!</h1>
<script src="script.js"></script>
</body>
</html>`;
const cssContent = `body { font-family: 'Arial', sans-serif; background-color: #e0f2f7; } h1 { color: #0277bd; text-align: center; }`;
const jsContent = `document.addEventListener('DOMContentLoaded', () => {
console.log('JavaScript processed by mowdown!');
const heading = document.querySelector('h1');
if (heading) heading.textContent = 'Mowdown has bundled this content!';
});`;
fs.writeFileSync(path.join(inputDir, 'index.html'), htmlContent);
fs.writeFileSync(path.join(inputDir, 'style.css'), cssContent);
fs.writeFileSync(path.join(inputDir, 'script.js'), jsContent);
// Define a minimal .babelrc required by mowdown
const babelrcContent = `{ "presets": ["@babel/preset-env"] }`;
fs.writeFileSync(path.join(__dirname, '.babelrc'), babelrcContent);
async function runBundler() {
try {
console.log('Running mowdown bundling process...');
await mowdown([path.join(inputDir, 'index.html')], outputDir, {
sourceFolder: inputDir,
isOverwritingHtml: false // Crucial to avoid overwriting original source HTML
});
console.log(`
✅ Mowdown finished successfully. Check '${outputDir}' for bundled files.`);
// Optional: Verify bundled HTML content
const bundledHtmlPath = path.join(outputDir, 'index.html');
if (fs.existsSync(bundledHtmlPath)) {
const bundledHtmlSnippet = fs.readFileSync(bundledHtmlPath, 'utf-8').substring(0, 500) + '...';
console.log('\nBundled index.html snippet:\n', bundledHtmlSnippet);
}
} catch (error) {
console.error('\n❌ Mowdown failed:', error.message);
} finally {
// Clean up temporary files and directories
fs.rmSync(inputDir, { recursive: true, force: true });
fs.rmSync(outputDir, { recursive: true, force: true });
fs.rmSync(path.join(__dirname, '.babelrc'), { force: true });
console.log('\nCleaned up temporary files and directories.');
}
}
runBundler();