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

> Learn to create distribution ZIP files for your Chrome extension using the zipper package. Stream your compiled extension files easily for the Chrome Web Store or Mozilla Add-ons.

- Repository: [JongHak Seo/chrome-extension-boilerplate-react-vite](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite)
- Tags: how-to-guide
- Published: 2026-03-05

---

**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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/zipper/package.json):

1. Build the extension:

   ```bash
   pnpm build
   ```

2. Run the zipper:

   ```bash
   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:

```typescript
// 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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/.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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/.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`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/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.