How to Use the Zipper Package to Create Distribution ZIP Files for the Extension

The zipper package is a lightweight Node.js utility that streams compiled extension files from the dist folder into a timestamped ZIP or XPI archive, ready for the Chrome Web Store or Mozilla Add-ons.

The chrome-extension-boilerplate-react-vite repository provides a modern Vite-based development environment for browser extensions. When your extension is built and ready for release, you need to create distribution ZIP files for the extension that exclude development artifacts and source maps. The built-in zipper package automates this process using streaming compression and intelligent file discovery.

Architecture of the Zipper Package

Core zipBundle Function

In packages/zipper/lib/zip-bundle.ts, the zipBundle function serves as the primary entry point for creating distribution archives. It accepts a configuration object specifying distDirectory, buildDirectory, and archiveName, plus a boolean flag to determine whether source map files should be preserved.

File Discovery with fast-glob

The utility uses fast-glob to recursively collect every file under the dist folder (lines 36-41 of zip-bundle.ts). By default, it filters out source map files (*.js.map, *.css.map) unless explicitly instructed otherwise via the function parameters.

Streaming Compression via fflate

Rather than loading files into memory, the package delegates reading to @extension/dev-utils's streamFileToZip function (lines 73-82). This pipes content directly into an fflate Zip instance, ensuring efficient memory usage even for large extensions. The function also calls ensureBuildDirectoryExists (lines 9-15) to verify the target directory exists before writing.

Archive Naming and Browser Detection

The CLI entry point at packages/zipper/index.mts determines the output filename format (extension-YYYYMMDD-HHMMSS.zip) and reads IS_FIREFOX from @extension/env to switch the extension to .xpi for Firefox distribution.

Creating Distribution ZIP Files

Method 1: Using the NPM Script

The simplest way to create distribution ZIP files for the extension is via the predefined script in packages/zipper/package.json:

  1. Build the extension:

    pnpm build
  2. Run the zipper:

    pnpm --filter @extension/zipper zip

This executes the compiled dist/index.mjs, loads environment variables from the repository root .env file, and invokes zipBundle with default paths pointing to dist/ and dist-zip/.

Method 2: Programmatic Usage

For custom build pipelines, import zipBundle directly into your Node.js scripts:

// scripts/create-zip.ts
import { resolve } from 'node:path';
import { zipBundle } from '@extension/zipper';

const now = new Date();
const timeStamp = now.toISOString().slice(0, 10).replace(/-/g, '') + '-' + 
                  now.toISOString().slice(11, 19).replace(/:/g, '');

await zipBundle(
  {
    distDirectory: resolve(__dirname, '../../dist'),
    buildDirectory: resolve(__dirname, '../../dist-zip'),
    archiveName: `extension-${timeStamp}.zip`,
  },
  false // set true to include *.js.map and *.css.map files
);

console.log('✅ Archive created successfully');

Run this with pnpm ts-node scripts/create-zip.ts.

Method 3: Customizing File Inclusion

To modify which files are bundled, inspect the source in packages/zipper/lib/zip-bundle.ts. The default implementation uses fast-glob to build its file list internally. For full control over file selection, you can create a wrapper script that pre-filters files and passes a custom list, though this requires modifying the internal zipBundle logic or reimplementing the streaming pattern using streamFileToZip from @extension/dev-utils.

CI/CD Integration

The repository includes a GitHub Actions workflow at .github/workflows/build-zip.yml that automates packaging. This workflow executes pnpm build followed by the zip script, then uploads the resulting archive from dist-zip/ as a build artifact, ensuring every pull request generates a testable extension package without manual intervention.

Summary

  • The zipper package lives in packages/zipper/ and exposes the zipBundle function for creating browser-specific archives.
  • It streams files from dist/ to dist-zip/ using fflate and fast-glob, excluding source maps by default (lines 36-41).
  • Run pnpm --filter @extension/zipper zip after building, or import the package programmatically for custom build pipelines.
  • The package automatically generates timestamped filenames and selects .zip or .xpi extensions based on the IS_FIREFOX environment variable.
  • Integration with GitHub Actions at .github/workflows/build-zip.yml enables automated distribution packaging in CI pipelines.

Frequently Asked Questions

What is the default output directory for the ZIP file?

By default, the zipper package writes archives to the dist-zip/ directory at the repository root. You can customize this by passing a different buildDirectory path to the zipBundle function in your custom scripts.

How do I include source map files in the distribution archive?

Pass true as the second argument to zipBundle. By default, this value is false, which causes the function to filter out *.js.map and *.css.map files during the glob discovery phase in zip-bundle.ts (lines 36-41).

Can I use the zipper package for Firefox extensions?

Yes. The package checks the IS_FIREFOX environment variable imported from @extension/env in packages/zipper/index.mts. When set to true, the generated archive uses the .xpi extension instead of .zip, making it compatible with Mozilla's add-on requirements.

Is the zipper package available as a standalone CLI tool?

The package is designed to run within the monorepo context. While you can execute it via pnpm --filter @extension/zipper zip, it depends on the specific directory structure (dist/, .env variables) and @extension/dev-utils helpers defined in this boilerplate. For standalone use outside this repository, you would need to replicate the environment setup and dependencies.

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 →