Skip to content

Latest commit

 

History

History
212 lines (168 loc) · 8.28 KB

AGENTS.md

File metadata and controls

212 lines (168 loc) · 8.28 KB

AGENTS.md

This file provides guidance to AI coding assistants when working with code in this repository.

Generated: 2026-01-28 | Commit: 9b0b0f5f | Branch: develop

Requirements

  • Node.js: >=22.0.0
  • Bun: 1.3.5+ (package manager & runtime)

Commands

Task Command
Install bun install
Build all bun run build
Build deps first bun run build:deps (utils)
Lint bun run lint
Format bun run format
Typecheck bun run typecheck
Test all bun run test
Test package bun run test packages/<name>
Test file bun run test packages/core/__tests__/core.test.ts
Doctor node-minify doctor
CI (full) bun run ci
Add changeset bun run changeset

Code Style (Biome)

  • Indent: 4 spaces | Quotes: double | Semicolons: always | Trailing commas: ES5
  • Imports: type keyword for type-only; node: prefix for Node.js built-ins
  • Extensions: Include .ts in local imports (e.g., ./setup.ts)
  • Naming: camelCase functions/variables, PascalCase types/interfaces
  • Headers: All source files: /*! node-minify ... MIT Licensed */
  • JSDoc: All exported functions MUST have @param and @returns tags

Architecture

Bun monorepo for compressing JS, CSS, HTML, JSON, and images.

Package Map

packages/
├── core/           # Main minify() function
├── utils/          # Shared utilities (see packages/utils/AGENTS.md)
├── types/          # TypeScript definitions (no build, exports src/types.d.ts)
├── cli/            # Commander.js CLI
├── benchmark/      # Compressor comparison tool
├── action/         # GitHub Action (see packages/action/AGENTS.md)
└── <compressor>/   # 22 compressor wrappers (terser, esbuild, lightningcss, etc.)

Compressors by Type

Type Recommended Alternatives
JS terser esbuild, swc, oxc, uglify-js, google-closure-compiler
CSS lightningcss esbuild, clean-css, cssnano, csso
HTML html-minifier minify-html
JSON jsonminify -
Images sharp (WebP/AVIF), svgo (SVG), imagemin (PNG/JPEG/GIF) -

Removed (v11): babel-minify, uglify-es, yui, crass, sqwish

Dependencies

core → utils + types
cli → core + benchmark + utils
action → core + benchmark + utils
benchmark → core + utils
compressors → utils + types

Build order: utils first → all others parallel

Package Pattern

packages/<name>/
├── src/index.ts      # Main export
├── __tests__/        # Vitest tests
├── package.json      # exports: { types, default }
├── tsconfig.json
└── vitest.config.ts

Exceptions:

  • types: No build, exports src/types.d.ts directly
  • action: Uses bun build (bundled), has action.yml
  • cli: Dual entry (src/index.ts + src/bin/cli.ts)
  • utils: Multi-file build (tsdown src/*.ts)

Key Types

From @node-minify/types:

Settings          // User options for minify()
MinifierOptions   // What compressors receive: { settings, content }
CompressorResult  // What compressors return: { code, map?, buffer?, outputs? }
Compressor        // Function: (args: MinifierOptions) => Promise<CompressorResult>

Adding a New Compressor

  1. Create packages/<name>/ with standard structure
  2. Export async function matching Compressor type:
import type { CompressorResult, MinifierOptions } from "@node-minify/types";
import { ensureStringContent, wrapMinificationError } from "@node-minify/utils";

export async function myCompressor({ settings, content }: MinifierOptions): Promise<CompressorResult> {
    const contentStr = ensureStringContent(content, "my-compressor");
    try {
        // ... minification logic
        return { code: minifiedCode };
    } catch (error) {
        throw wrapMinificationError("my-compressor", error);
    }
}
  1. Add tests using runOneTest from tests/fixtures.ts
  2. Add changeset: bun run changeset

Testing

Shared fixtures (tests/fixtures.ts):

  • runOneTest({ options, compressorLabel, compressor }) — Run single test
  • tests object — Pre-defined suites: concat, commonjs, commoncss, commonhtml, commonjson

File paths (tests/files-path.ts):

  • filesJS, filesCSS, filesHTML, filesJSON, filesImages

Convention: Tests auto-isolate to tests/tmp/ when using replaceInPlace or $1 output patterns.

Timeouts: Some packages use 60s timeout for slow compressors.

Changesets

Required for user-facing changes:

bun run changeset           # Add changeset (interactive)
bun run changeset:version   # Bump versions

Custom scripts/publish.ts resolves workspace:* → concrete versions before npm publish.

Anti-Patterns (DO NOT)

  • Never use any — not as as any, not as an annotation, not as a type argument. Biome's noExplicitAny is set to error and fires on every explicit any in any position, so the gate is broader than the as any symptom it was introduced for.

  • Never use as unknown as T anywhere. No linter catches this shape, so it is enforced by review: use a real type, or @ts-expect-error where the invalidity is the point

  • Never use @ts-ignore or @ts-nocheck anywhere — both silently disable checking rather than proving an error exists

  • Never use @ts-expect-error in src/ — fix the type instead

  • In test files only, @ts-expect-error is sanctioned for two cases, each of which must carry a comment on the same line naming the specific condition:

    // @ts-expect-error testing invalid input: settings is missing required fields
    // @ts-expect-error mock is narrower than the real type it stands in for

    Test files are typechecked (bun run typecheck), so an unused directive fails the build — the suppression cannot rot. This covers every __tests__/ directory plus the shared tests/ helpers and tests/integration/.

    Place the directive on the line the compiler reports, which is not always the offending property: a missing required field is reported on the object-literal or declaration line, so the directive belongs there rather than on a property. Where several errors would be reported for one literal, TypeScript surfaces only the first — keep the comment to the error actually being suppressed instead of listing every latent one.

    Enforcement of these four rules is split. Biome's noTsIgnore catches @ts-ignore, and root lint covers workspace packages plus tests/, scripts/, and examples/ via lint:root. scripts/ci-guard-type-suppressions.sh is the mechanism for what no Biome rule covers: it bans @ts-nocheck and @ts-ignore across the whole repository, bans @ts-expect-error under any src/, and rejects a directive with no reason after it. Run it locally with bash scripts/ci-guard-type-suppressions.sh; CI runs it on the Linux/Node 22 leg.

  • Never remove JSDoc from exported functions

  • Never use deprecated packages in new code

  • Never commit without bun run lint

  • Never use sync file operations in compressor code (use async)

  • Never mix image and text files in same input array

GitHub Action

Published as srod/node-minify@v11. See packages/action/AGENTS.md for details.

- uses: srod/node-minify@v11
  with:
    input: "src/app.js"
    output: "dist/app.min.js"
    compressor: "terser"

Zero-config mode:

- uses: srod/node-minify@v11
  with:
    auto: true
    output-dir: dist

CI/CD

Workflow Trigger Purpose
test.yml Push/PR Build, lint, typecheck, test
publish.yml Push to main Changesets release + npm publish
test-action.yml Action changes GitHub Action integration tests
release-action.yml Release Bundle action dist, update tags

Documentation Site

docs/ is a separate Astro site. See docs/AGENTS.md.

cd docs && bun run dev    # Dev server at localhost:4321
cd docs && bun run build  # Production build

Troubleshooting

Issue Fix
Build fails bun run build:deps first
Type errors Run bun run build (types from dist/)
Test isolation Uses tests/tmp/ (gitignored)
Clean rebuild bun run clean && bun run build