Contributing

Dev setup, test layout, the parallel-sessions git workflow, and the full pre-PR checklist.

Topics: Anatomy Building Parsing Validation URL Ecosystems Comparison Security Architecture Builders Contributing Converters Hardening Release Tour VERS

Contributing

What you need to know before opening a PR against @socketregistry/packageurl-js. Dev setup, the test/coverage/lint workflow, the parallel-session git rules, and the pre-PR checklist.

Who this is for #

First-time contributors and returning ones who want to refresh on the commands and conventions. Reading order: Setup → Making a change → Running checks → Opening the PR.

Setup #

Requirements:

Tool Version Notes
Node.js ≥ 22 Built-in TypeScript stripping (--experimental-strip-types on 22, default on 23+).
pnpm ≥ 11.0.0-rc.0 Install via corepack enable pnpm or npm install -g pnpm.
git ≥ 2.30 Submodule support.

Clone and install:

git clone https://github.com/SocketDev/socket-packageurl-js.git
cd socket-packageurl-js
pnpm install

pnpm install also:

pnpm tour doctor reports which external tools the build may shell out to are present vs missing on your machine. All listed tools are optional; missing ones fall back to safe defaults.

Repo layout #

socket-packageurl-js/
├── src/                    ← library source (see docs/architecture.md)
├── test/                   ← functional tests (vitest)
├── scripts/                ← build/lint/test/release scripts
├── docs/                   ← these docs
├── val/                    ← Val Town comment backend (tour-specific)
├── upstream/meander/       ← submodule (tour generator)
├── .config/                ← tsconfig + vitest configs
├── .github/workflows/      ← CI (ci.yml, pages.yml, provenance.yml, ...)
├── .claude/                ← Claude Code config (agents, skills, hooks)
├── tour.json               ← tour manifest
└── pages/                  ← tour build output (gitignored)

You almost never edit upstream/meander/ (that's the submodule we pin) or pages/ (build output).

Making a change #

1. Branch, worktree, or sibling clone #

This repo expects multiple concurrent Claude Code sessions may be working on the same checkout. Plain git checkout -b inside the main clone yanks the working tree out from under any other session. Use one of:

See the PARALLEL CLAUDE SESSIONS section of CLAUDE.md for the full doctrine.

2. Edit source #

Per CLAUDE.md:

3. Write tests #

Two vitest configs:

Config When File naming
.config/vitest.config.mts Normal tests. Threads, shared memory. Fast. *.test.mts
.config/vitest.config.isolated.mts Tests that mock globals via vi.doMock, modify process.env, or process.chdir. Forks, full isolation. *.isolated.test.mts

Test style in this repo: functional. Tests assert behavior via the public API — inputs → outputs. Tests never read source files and assert on their contents.

Example:

import { test, expect } from 'vitest'
import { PackageURL } from '../src/package-url.js'

test('parses npm scoped package', () => {
  const purl = new PackageURL('pkg:npm/@scope/pkg@1.0.0')
  expect(purl.namespace).toBe('@scope')
  expect(purl.name).toBe('pkg')
  expect(purl.version).toBe('1.0.0')
})

If you are adding a new ecosystem or URL parser, put tests under test/purl-types/<name>.test.mts or test/url-converter/<name>.test.mts.

Running checks #

The canonical pre-PR check is a single command:

pnpm check

It runs:

  1. pnpm type — tsgo strict type-check.
  2. pnpm lint — oxlint across the tree.
  3. pnpm test — vitest (both configs).
  4. Format verification.

Every step runs independently too:

Command What
pnpm build Compile src/dist/ (esbuild). pnpm build --watch for dev.
pnpm type Strict TypeScript check, no emit.
pnpm lint oxlint.
pnpm fix Auto-fix what's auto-fixable (formatter + lint autofixes).
pnpm test Run vitest. pnpm test:unit path/to/file.test.mts for a single file. Never use -- before test paths — runs ALL tests.
pnpm testu Update vitest snapshots (review the diff before committing).
pnpm cover Coverage. Must stay at 100%.
pnpm format Run oxfmt across the tree (writes fixes).
pnpm format --check Verify formatting without writing.
pnpm security AgentShield + zizmor security scan.

Coverage — the 100% rule #

pnpm cover must report 100% line/branch/function coverage.

If you add a code path, add a test that exercises it. If you discover a corner case while reading, add a test for it even if the coverage number already says 100% (percentages can miss edge cases).

When coverage drops, the failure names the file and the missed line/branch. Add a test or document an /* c8 ignore next */ with a justification comment.

Snapshot tests #

pnpm testu regenerates snapshots. Never run this unless the snapshot drift is intentional — otherwise you are deleting future- you's safety net. Review the snapshot diff before committing.

The parallel-session git rules #

Forbidden in the primary checkout (the one another session may be editing):

Required for branch work:

git worktree add -b <task-branch> ../<repo>-<task> main
cd ../<repo>-<task>
# edit, commit, push from here
cd -
git worktree remove ../<repo>-<task>

Required for staging:

git add <specific-file> [<file>…]

Never -A / ..

For a quick WIP save: commit on a new branch from inside a worktree, not a stash.

Commit messages #

Conventional Commits:

<type>(<scope>): <description>

<optional body>

<optional footer>

Types used in this repo:

Scopes are free-form but stable: purl-types/npm, url-converter, tour, hardening. One commit = one concern. If the body explains both a refactor and a fix, they should probably be two commits.

Never add AI attribution ("Co-authored-by: Claude", etc.) to commit messages. Humans commit; tools assist.

Opening a PR #

After pnpm check passes and all commits are conventional:

gh pr create --title "<conventional title>" --body "…"

The PR body should:

CI will run:

Pre-PR checklist #

Copy into your PR description and tick off:

Hazards #

Things that have caught contributors before: