CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-unzipper

Cross-platform streaming API for ZIP file extraction and manipulation in Node.js environments

Pending
Overview
Eval results
Files

parse-one.mddocs/

Single File Extraction

Specialized ZIP parser for extracting only the first matching file from an archive. This is ideal for scenarios where you need specific files without processing the entire archive, providing efficient targeted extraction.

Capabilities

ParseOne Constructor

Creates a parser that stops after finding and processing the first file matching the specified criteria.

/**
 * Creates a parser that extracts only the first matching file
 * @param match - Optional regular expression to match file paths
 * @returns Parse stream instance configured for single file extraction
 */
function ParseOne(match?: RegExp): Parse;

Usage Examples:

const unzipper = require("unzipper");
const fs = require("fs");

// Extract first file found
fs.createReadStream("archive.zip")
  .pipe(new unzipper.ParseOne())
  .on("entry", (entry) => {
    entry.pipe(fs.createWriteStream("first-file.txt"));
  });

// Extract first .json file
fs.createReadStream("config-archive.zip")
  .pipe(new unzipper.ParseOne(/\.json$/))
  .on("entry", (entry) => {
    console.log(`Found config file: ${entry.path}`);
    entry.pipe(fs.createWriteStream("config.json"));
  });

// Extract first README file (case insensitive)
fs.createReadStream("project.zip")
  .pipe(new unzipper.ParseOne(/readme\.(txt|md)$/i))
  .on("entry", (entry) => {
    entry.pipe(fs.createWriteStream("README"));
  });

Pattern Matching

The optional RegExp parameter allows precise control over which file to extract.

Pattern Examples:

// Extract first image file
new unzipper.ParseOne(/\.(jpg|jpeg|png|gif)$/i)

// Extract main configuration file
new unzipper.ParseOne(/^config\.(json|yaml|yml)$/)

// Extract any file in root directory
new unzipper.ParseOne(/^[^\/]+$/)

// Extract specific file by exact path
new unzipper.ParseOne(/^src\/main\.js$/)

// Extract first file in specific directory
new unzipper.ParseOne(/^data\//)

Inherited Capabilities

ParseOne returns a Parse instance configured for single-file extraction, providing all Parse methods and events with automatic termination after processing the first matching entry.

Promise Interface

/**
 * Returns a Promise that resolves when the first matching file is found and processed
 * @returns Promise resolving when extraction completes
 */
promise(): Promise<void>;

Stream Management

/**
 * Drains the stream (automatically called after first match)
 */
autodrain(): void;

/**
 * Buffers the stream contents
 */
buffer(): void;

Events

ParseOne emits the same events as Parse, but only for the first matching entry:

// Single entry event for the matched file
parseOne.on('entry', (entry: Entry) => {
  // This will only fire once for the first matching file
  console.log(`Matched file: ${entry.path}`);
});

// Standard stream events
parseOne.on('finish', () => {
  console.log('Single file extraction completed');
});

parseOne.on('error', (error: Error) => {
  console.error('Extraction error:', error);
});

Advanced Usage

Extract with Content Processing

const unzipper = require("unzipper");

// Extract and process first JSON config file
fs.createReadStream("app-bundle.zip")
  .pipe(new unzipper.ParseOne(/\.json$/))
  .on("entry", (entry) => {
    let jsonContent = '';
    
    entry.on('data', (chunk) => {
      jsonContent += chunk.toString();
    });
    
    entry.on('end', () => {
      try {
        const config = JSON.parse(jsonContent);
        console.log('Loaded config:', config);
        
        // Save processed config
        fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
      } catch (error) {
        console.error('Invalid JSON:', error);
      }
    });
  });

Conditional Extraction with Validation

// Extract first valid package.json file
fs.createReadStream("node-modules.zip")
  .pipe(new unzipper.ParseOne(/package\.json$/))
  .on("entry", (entry) => {
    const chunks = [];
    
    entry.on('data', (chunk) => {
      chunks.push(chunk);
    });
    
    entry.on('end', () => {
      const content = Buffer.concat(chunks).toString();
      
      try {
        const packageInfo = JSON.parse(content);
        
        if (packageInfo.name && packageInfo.version) {
          console.log(`Found package: ${packageInfo.name}@${packageInfo.version}`);
          fs.writeFileSync('package.json', content);
        } else {
          console.log('Invalid package.json format');
        }
      } catch (error) {
        console.error('Failed to parse package.json:', error);
      }
    });
  });

Multiple Attempts with Error Handling

const extractFirstMatch = (zipPath, patterns) => {
  return new Promise((resolve, reject) => {
    let patternIndex = 0;
    
    const tryPattern = () => {
      if (patternIndex >= patterns.length) {
        return reject(new Error('No matching files found'));
      }
      
      const pattern = patterns[patternIndex];
      let found = false;
      
      fs.createReadStream(zipPath)
        .pipe(new unzipper.ParseOne(pattern))
        .on("entry", (entry) => {
          found = true;
          resolve({ entry, pattern });
        })
        .on("finish", () => {
          if (!found) {
            patternIndex++;
            tryPattern();
          }
        })
        .on("error", reject);
    };
    
    tryPattern();
  });
};

// Usage
extractFirstMatch('archive.zip', [
  /readme\.md$/i,
  /readme\.txt$/i,
  /readme$/i
]).then(({ entry, pattern }) => {
  console.log(`Found README with pattern: ${pattern}`);
  entry.pipe(fs.createWriteStream('README.md'));
}).catch(error => {
  console.error('No README found:', error);
});

Install with Tessl CLI

npx tessl i tessl/npm-unzipper

docs

extraction.md

index.md

open.md

parse-one.md

parsing.md

tile.json