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

read-pyproject

v0.3.1

Published

Parse and normalize pyproject.toml metadata into strictly typed JavaScript objects.

Readme

read-pyproject

NPM Package read-pyproject License: MIT

Parse and normalize pyproject.toml metadata into strictly typed JavaScript objects.

Overview

A typed pyproject.toml reader for Node.js.

Highlights:

  • Typed output
    The returned object is deeply typed via Zod schema inference, giving you autocomplete and type safety for [project], [build-system], [dependency-groups], and 30+ [tool.*] sections.

  • Normalization
    The mess of kebab-case, snake_case, and PascalCase keys are converted to camelCase in the output by default, and some other fields (like license and readme values) are sensibly normalized.

  • Flexibly strict
    Control how unknown keys are handled with three modes: 'passthrough' (default, keeps everything), 'strip' (silently removes unknown keys), or 'error' (throws on unknown keys).

  • Broad tool coverage
    Typed schemas for 30+ common [tool.*] sections. Unrecognized tools pass through as unknown by default.

Note that this library currently only reads, it does not write changes back to the .toml file.

Getting started

Dependencies

Node 20.17.0+

Installation

npm install read-pyproject

Quick start

import { readPyproject } from 'read-pyproject'

const pyproject = await readPyproject('/path/to/project')

console.log(pyproject.project?.name) // Normalized PEP 503 name
console.log(pyproject) // The rest of the object...

Or parse a TOML string directly:

import { parsePyproject } from 'read-pyproject'

const toml = `
[project]
name = "my-package"
version = "1.0.0"
`

const pyproject = parsePyproject(toml)

Usage

API

readPyproject(pathOrDirectory?, options?)

Read, parse, validate, and normalize a pyproject.toml file.

Parameters
  • pathOrDirectory — A file path or directory. If a directory, appends /pyproject.toml. Defaults to process.cwd().
  • options — Optional configuration object:
    • camelCase — Convert keys to camelCase (true by default). Set to false to get raw TOML keys.
    • unknownKeyPolicy — How to handle unknown keys: 'passthrough' (default), 'strip', or 'error'.
Returns
  • Promise<PyprojectData> when camelCase is true (default)
  • Promise<RawPyprojectData> when camelCase is false

The return type is inferred from the camelCase option via function overloads.

Throws

PyprojectError on missing files, invalid TOML, or validation failures (in 'error' mode).

Examples
import { readPyproject } from 'read-pyproject'

// Read from current directory (camelCase keys by default)
await readPyproject()

// Read from a specific file
await readPyproject('/path/to/pyproject.toml')

// Read from a directory
await readPyproject('/path/to/project')

// Get raw TOML keys (no camelCase conversion)
const raw = await readPyproject('/path/to/project', { camelCase: false })
raw['build-system']?.['build-backend'] // Raw kebab-case keys

// Reject unknown keys
await readPyproject('/path/to/project', { unknownKeyPolicy: 'error' })

// Strip unknown keys from the output
await readPyproject('/path/to/project', { unknownKeyPolicy: 'strip' })

parsePyproject(content, options?)

Parse, validate, and normalize a pyproject.toml content string. This is the synchronous counterpart to readPyproject — it accepts a TOML string instead of reading from the filesystem.

Parameters
  • content — A pyproject.toml content string.
  • options — Optional configuration object:
    • camelCase — Convert keys to camelCase (true by default). Set to false to get raw TOML keys.
    • unknownKeyPolicy — How to handle unknown keys: 'passthrough' (default), 'strip', or 'error'.
Returns
  • PyprojectData when camelCase is true (default)
  • RawPyprojectData when camelCase is false

The return type is inferred from the camelCase option via function overloads.

Throws

PyprojectError on invalid TOML or validation failures (in 'error' mode).

Examples
import { parsePyproject } from 'read-pyproject'

// Parse a TOML string (camelCase keys by default)
const pyproject = parsePyproject('[project]\nname = "my-package"')

// Get raw TOML keys (no camelCase conversion)
const raw = parsePyproject(tomlString, { camelCase: false })

// Reject unknown keys
parsePyproject(tomlString, { unknownKeyPolicy: 'error' })

PyprojectError

Custom error class thrown for file read errors, TOML parse errors, and validation failures. Includes a filePath property for context.

setLogger(logger?)

Inject a custom logger. Accepts a LogLayer instance or a Console-like object.

Normalization

  • All kebab-case, snake_case, and PascalCase keys in the TOML are converted to camelCase in the output by default. Pass { camelCase: false } to disable this and receive raw TOML keys instead.

  • project.name is normalized per PEP 503 (lowercased, runs of [-_.]+ collapsed to a single -). The original name is available as project.rawName. This normalization is always applied regardless of the camelCase option.

  • project.readme is normalized to a string when the readme is file-based ("README.md" stays as-is, { file: "README.md", content-type: "..." } collapses to "README.md"). Inline-text readmes ({ text: "...", content-type: "..." }) are kept as a { text, contentType? } object.

  • project.license is normalized from an SPDX string ("MIT") to { spdx }, validated and corrected via spdx-correct. Legacy table-form licenses ({ file } or { text }) pass through as-is.

Supported [tool.*] sections

The following tools have typed schemas:

autopep8, bandit, black, bumpversion, check-wheel-contents, cibuildwheel, codespell, comfy, commitizen, coverage, dagster, distutils, docformatter, flake8, flit, hatch, isort, jupyter-releaser, mypy, pdm, pixi, poe, poetry, pydocstyle, pylint, pyright, pytest, ruff, setuptools, setuptools_scm, tbump, towncrier, uv, yapf

Unknown tools pass through as unknown by default.

Background

Motivation

I wanted something like read-pkg or pkg-types, but for pyproject.toml files.

It's a bit strange to work across language ecosystems like this, but I had occasion to do so for some other Node-based projects related to project metadata extraction, specifically metascope.

Implementation notes

The project consists of a number of Zod schemas responsible for validating and normalizing data found in a pyproject.toml. Schemas output raw TOML keys; camelCase conversion is handled centrally by a recursive deepCamelCaseKeys function that knows which paths contain user-defined record keys (like package names or file paths) and skips those. Type-level camelCase conversion uses CamelCasedPropertiesDeep from type-fest, and function overloads ensure the return type matches the camelCase option.

Forcing the rather dynamic and extensible data structure found in pyproject.toml into a TypeScript straightjacket is likely futile, but an LLM makes the project at least somewhat tractable.

Maintainers

@kitschpatrol

Slop factor

High.

An initial human-crafted specification was implemented by LLM. The output has been subject to only moderate post-facto human scrutiny.

Contributing

Issues and pull requests are welcome.

License

MIT © Eric Mika