code icon Code

Zip Archive

Create or extract zip archives

Source Code

import fs from "fs";
import path from "path";
import archiver from "archiver";
import unzipper from "unzipper";

const [mode, inputPath, outputPath] = process.argv.slice(2);

if (!mode || !inputPath || !outputPath) {
  console.error("Usage: mode inputPath outputPath");
  console.error("  create: inputPath is file/directory, outputPath is .zip");
  console.error("  extract: inputPath is .zip, outputPath is directory");
  process.exit(1);
}

async function createZip(source, dest) {
  console.log(`Creating zip archive...`);
  console.log(`  Source: ${source}`);
  console.log(`  Destination: ${dest}`);

  // Ensure output directory exists
  const dir = path.dirname(dest);
  if (dir && dir !== ".") {
    fs.mkdirSync(dir, { recursive: true });
  }

  const output = fs.createWriteStream(dest);
  const archive = archiver("zip", { zlib: { level: 9 } });

  return new Promise((resolve, reject) => {
    output.on("close", () => {
      resolve(archive.pointer());
    });

    archive.on("error", reject);
    archive.pipe(output);

    const stats = fs.statSync(source);

    if (stats.isDirectory()) {
      archive.directory(source, false);
    } else {
      archive.file(source, { name: path.basename(source) });
    }

    archive.finalize();
  });
}

async function extractZip(source, dest) {
  console.log(`Extracting zip archive...`);
  console.log(`  Source: ${source}`);
  console.log(`  Destination: ${dest}`);

  // Ensure output directory exists
  fs.mkdirSync(dest, { recursive: true });

  let fileCount = 0;

  return new Promise((resolve, reject) => {
    fs.createReadStream(source)
      .pipe(unzipper.Parse())
      .on("entry", (entry) => {
        const filePath = path.join(dest, entry.path);

        if (entry.type === "Directory") {
          fs.mkdirSync(filePath, { recursive: true });
          entry.autodrain();
        } else {
          fs.mkdirSync(path.dirname(filePath), { recursive: true });
          entry.pipe(fs.createWriteStream(filePath));
          fileCount++;
        }
      })
      .on("close", () => resolve(fileCount))
      .on("error", reject);
  });
}

try {
  let result;

  if (mode === "create") {
    if (!fs.existsSync(inputPath)) {
      console.error(`Source not found: ${inputPath}`);
      process.exit(1);
    }

    const bytes = await createZip(inputPath, outputPath);
    const stats = fs.statSync(outputPath);

    console.log(`\nāœ“ Created zip archive`);
    console.log(`  Size: ${(stats.size / 1024).toFixed(1)} KB`);
    console.log(`  Written to: ${outputPath}`);

    result = {
      success: true,
      mode: "create",
      inputPath,
      outputPath,
      bytes: stats.size,
    };
  } else if (mode === "extract") {
    if (!fs.existsSync(inputPath)) {
      console.error(`Archive not found: ${inputPath}`);
      process.exit(1);
    }

    const fileCount = await extractZip(inputPath, outputPath);

    console.log(`\nāœ“ Extracted zip archive`);
    console.log(`  Files: ${fileCount}`);
    console.log(`  Extracted to: ${outputPath}`);

    result = {
      success: true,
      mode: "extract",
      inputPath,
      outputPath,
      fileCount,
    };
  } else {
    console.error(`Unknown mode: ${mode}. Use: create, extract`);
    process.exit(1);
  }

  console.log(JSON.stringify(result));
} catch (error) {
  console.error("Error:", error.message);
  process.exit(1);
}