npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@woss/exiftool

v0.7.3

Published

ExifTool rewrite in TypeScript

Readme

exiftool-ts

TypeScript rewrite of ExifTool for Node.js. Read and write metadata from JPEG, PNG, WebP, AVIF/HEIF, and TIFF-family images — with a fully typed library API and a drop-in CLI.

Why

ExifTool is the gold standard for metadata — but it's a 30k-line Perl program. Every invocation pays a Perl startup cost, embedding it in a JS/TS service means shelling out or managing sidecars, and there are no types.

exiftool exists to bring that capability natively into the TypeScript ecosystem:

  • Typed end to end — read() returns an inferred FileInfo; no parsing strings.
  • Embeddable — a single runtime dependency (fflate, for PNG chunk compression); import the library directly or ship the CLI alongside your service. No child processes.
  • Trust ExifTool as ground truth — our parity suite diffs our output against the real exiftool binary on every run; formatting bugs get caught, not shipped.
  • Auto-generated tag database — tag definitions are parsed from the ExifTool Perl source at build time (~thousands of tags), not hand-maintained.

Status & parity

Early development, moving fast. The full, maintained status matrix lives in the parity docs and the working register in DIVERGENCES.md.

Value-level parity is enforced by src/cli/exiftool-parity.test.ts, which runs the real exiftool binary on shared fixtures and fails on any undocumented divergence. Remaining gaps are registered in KNOWN_DIVERGENCES inside that file with reasons (file-date timezone offsets, makernote lens lookups, …).

Install

npm (CLI + library, Node ≥ 18):

npm i -g @woss/exiftool
exiftool-ts photo.jpg

The package ships compiled JS with full .d.ts types and a single runtime dependency (fflate).

JSR (for Deno consumers, runs from TypeScript source):

deno install -A -n exiftool-ts jsr:@woss/exiftool

Library usage

The package ships .d.ts declarations, so consumers get full types and autocomplete out of the box:

import { ExifTool } from "@woss/exiftool";

const tool = new ExifTool();

const info = await tool.read("photo.jpg");
info.tags.Make; // "Canon"        — typed as TagValue
info.tags.ExposureTime; // "1/200"        — formatted like exiftool
Object.keys(info.tags); // browse everything

// In-memory buffers work too:
const meta = await tool.readBytes(imageBuffer);

// Writing (creates photo.jpg_original unless suppressed):
await tool.write("photo.jpg", { Artist: "me", Copyright: "(c)" });

Exported surface: ExifTool, TagDb, writeTags, UnsupportedFormatError, and the types FileInfo, TagValue, WriteResult, ParseHints, ReadOptions, TagEntry, TagGroups.

Plugins

Formats are plugins. By default ExifTool loads every built-in one, but you can restrict an instance to a set — the bundler then ships only those parsers:

import { ExifTool } from "@woss/exiftool";
import { MODERN_PLUGINS } from "@woss/exiftool/plugins";

const tool = new ExifTool({ plugins: MODERN_PLUGINS }); // JPEG, PNG, GIF, WebP, AVIF — ~91% of web image traffic

Three presets ship from the plugins subpath: MODERN_PLUGINS (web formats, per HTTP Archive's Web Almanac 2024), MODERN_RAW_PLUGINS (DNG/CR2/NEF/ARW/ORF/RW2 RAW containers plus .xmp sidecar support) and ALL_PLUGINS (every shipped parser, in detection order). Individual parsers (jpegParser, pngParser, webpParser, avifParser, tiffRawParser) and fully custom plugins ({ format, extensions, canParse, parse, writeBytes? }) come from the same subpath. A plugin without writeBytes is read-only. The default — every built-in — loads lazily, so new ExifTool() keeps working unchanged.

Browser

The parser core is platform-free. The browser export condition resolves to a bundle with no Node builtins; reads and writes work on in-memory buffers:

import { ExifTool, MODERN_PLUGINS } from "@woss/exiftool/browser";

const tool = new ExifTool({ plugins: MODERN_PLUGINS });
const { bytes, written } = await tool.writeBytes(imageBuffer, { Artist: "me" });
const info = await tool.readBytes(bytes);

Path-based read/write stay Node-only (main entry); browser consumers get readBytes/writeBytes.

CLI usage

exiftool-ts photo.jpg                           # default tabular dump
exiftool-ts -j photo.jpg                        # JSON
exiftool-ts -X photo.jpg > out.xml              # XML
exiftool-ts -csv *.jpg > out.csv                # CSV
exiftool-ts -r -ext jpg .                       # recurse a directory tree
exiftool-ts -if '$Make eq Canon' *.jpg          # condition filter
exiftool-ts -ee -j multi-picture.jpg            # embedded images as extra docs
exiftool-ts '-Artist=me' photo.jpg              # write a tag (_original backup)
exiftool-ts --overwrite-original '-Software=x' photo.jpg
printf -- '-j\nphoto.jpg\n-execute\n-stay_open\nFalse\n' \
  | exiftool-ts -stay_open True                 # persistent daemon

Development

pnpm test                  # run the full test suite
pnpm check                 # typecheck
pnpm coverage-audit        # tests + enforce 100% line/function coverage
pnpm generate-tags         # regenerate tag DB (requires exiftool source)

# Parity vs real exiftool (needs `exiftool` on PATH):
npx vitest run src/cli/exiftool-parity.test.ts

Project scope

Near term: deepen XMP/IPTC writing, extend the container list toward the formats exiftool covers. EXIF (IFD0/ExifIFD/GPS) writing and MakerNote decoding for the core 6 vendors are shipped. The long-term target remains parity with ExifTool's reading surface across its supported formats — tracked in the parity docs.

License

MIT — © woss (woss.io), covering this repository's TypeScript code only.

One exception, documented in NOTICE and src/tags/generated/LICENSE.md: the embedded tag-database JSON (src/tags/generated/) is derived from Phil Harvey's ExifTool (exiftool -listx tables and PrintConv values) and is subject to ExifTool's own license — GPL-3.0-or-later or Artistic License 2.0 — redistributed here under the Artistic License 2.0.

Credits

ExifTool was created by Phil Harvey, who maintains it single-handedly since 2003. This project exists because of his work and would not be possible without it. The TypeScript code is an independent implementation that uses his tool only as a behavioral reference for output parity; the embedded tag-database tables are derived from ExifTool and carried under his dual license (see License above). All credit for the ExifTool concept, design, and decades of metadata expertise belongs to him. Consider supporting his work.


Made with Human and AI effort — woss.io