How to Build and Package the Markdown Here Extension Bundle

The Markdown Here repository uses a Node.js build script at utils/build.js to assemble browser-specific extension packages by filtering source files, adjusting manifests, and generating zip archives for Chrome, Firefox, and Thunderbird.

The adam-p/markdown-here project provides a streamlined build system for creating cross-browser extension bundles from a shared source tree. To build and package the Markdown Here extension bundle correctly, developers run a single Node-based script that handles platform-specific file selection, manifest modifications, and archive compression automatically.

Build System Architecture

The build orchestration lives entirely within utils/build.js, which defines the core directory structure and platform targeting logic. At lines 15-18, the script establishes three essential constants: BASE_DIR (repository root), SRC_DIR (the src/ source tree), and DIST_DIR (the output folder where bundles are written).

Platform-specific input arrays determine which files enter each bundle. The script defines CHROME_INPUT, FIREFOX_INPUT, and THUNDERBIRD_INPUT globs at lines 23-26 to segment the source code appropriately. A skipFileRegexes array defined at lines 31-34 filters out test files, OS-specific temporary files, and other non-production assets before packaging begins.

Step-by-Step Build Process

1. Directory Setup and File Filtering

The build begins by establishing the output structure and filtering rules. The setUpZips() function (lines 74-92 and 94-111) initializes three separate archiver zip streams—one for each target platform—and removes any previous bundle files from dist/. This ensures clean builds without stale artifacts contaminating the output.

File selection respects platform boundaries through the input globs defined earlier. When the main() function walks the src/ directory tree (lines 24-44 and 48-56), it tests each file against the platform-specific input arrays. Valid files are passed to addBuildFile(), which routes them into the appropriate archive stream based on the current platform context.

2. Platform-Aware Manifest Handling

Chrome, Opera, and WebExtension-based browsers require strict manifest validation. When packaging for Chrome, the build script performs a critical transformation at lines 62-67: it strips the Firefox-only browser_specific_settings block from src/manifest.json. This ensures Chrome accepts the manifest without validation errors. All other files are added to the archives unchanged, preserving the integrity of shared logic in src/common/ and platform-specific scripts in src/chrome/, src/firefox/, and src/thunderbird/.

3. Archive Finalization

After main() completes its directory traversal and queues all eligible files, the script executes the finalization phase at lines 46-48. Each zip stream is sealed and piped to its target destination, producing three distinct bundles: dist/chrome.zip for Chrome/Opera/WebExtension, dist/firefox.zip for Firefox, and dist/thunderbird.xpi for Thunderbird.

Running the Build Commands

Execute the following commands from the repository root to install dependencies and trigger the build process:


# Clone the repository

git clone https://github.com/adam-p/markdown-here.git
cd markdown-here

# Install Node.js dependencies (run once)

npm install --prefix utils

# Build all extension bundles

cd utils
node build.js

Upon completion, the console outputs:


Done! Built extensions written to ../dist

Build Output and Bundle Structure

The build generates three platform-specific archives in the dist/ directory:

  • ../dist/chrome.zip – Contains the Chrome, Opera, and generic WebExtension bundle
  • ../dist/firefox.zip – Contains the Firefox-specific extension package
  • ../dist/thunderbird.xpi – Contains the Thunderbird email client extension

Each bundle includes the shared src/common/ assets (core logic, UI components, and icons) combined with platform-specific scripts from src/chrome/, src/firefox/, or src/thunderbird/ as appropriate. The README.md "Building the Extension Bundles" section (lines 70-76) documents these steps for developer reference, mirroring the automated logic implemented in the build script.

Summary

  • Single script orchestration: The utils/build.js file handles all packaging logic through its main() and setUpZips() functions.
  • Platform-specific filtering: Input globs (CHROME_INPUT, FIREFOX_INPUT, THUNDERBIRD_INPUT) and skipFileRegexes ensure only relevant files enter each bundle.
  • Manifest compatibility: The build automatically strips Firefox-specific browser_specific_settings from the manifest when targeting Chrome to prevent validation errors.
  • Three-bundle output: Running node build.js produces chrome.zip, firefox.zip, and thunderbird.xpi in the dist/ directory.
  • Source separation: Shared code lives in src/common/, while browser-specific implementations reside in src/chrome/, src/firefox/, and src/thunderbird/.

Frequently Asked Questions

What Node.js version is required to build the Markdown Here extension?

The build script uses standard Node.js file system and stream APIs available in Node.js 8.0 and higher. As long as you can run npm install successfully in the utils/ directory, the build process will execute correctly using the archiver package dependency specified in the project.

Can I modify the build to exclude certain files from only one platform bundle?

Yes. Edit the CHROME_INPUT, FIREFOX_INPUT, or THUNDERBIRD_INPUT arrays at lines 23-26 in utils/build.js. Each array accepts glob patterns that define which source directories and files are eligible for that specific platform. Files matching the skipFileRegexes pattern are excluded from all bundles automatically.

Why does the Chrome bundle need manifest modifications while Firefox does not?

Chrome and WebExtension-standard browsers reject unknown manifest keys such as browser_specific_settings, which Firefox requires for extension signing and update URLs. The build script's logic at lines 62-67 programmatically removes this Firefox-only block when assembling chrome.zip, ensuring the resulting manifest passes Chrome's strict validation without maintaining separate manifest files in the source tree.

How do I verify the build output before distribution?

After running node build.js, inspect the contents of dist/chrome.zip, dist/firefox.zip, and dist/thunderbird.xpi using any archive utility. Verify that manifest.json contains platform-appropriate keys (no browser_specific_settings in the Chrome version) and that platform-specific scripts from src/chrome/ or src/firefox/ appear in their respective bundles while being absent from others.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →