Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# AGENTS.md

Guide for AI coding agents (Claude Code and others) working in this repository. This is the source of truth β€” `CLAUDE.md` points here.

## Project

`@phpdevsr/js-validation` β€” lightweight, zero-dependency vanilla JavaScript form validation library inspired by `jquery-validation`. Ships as UMD + ES module, no runtime deps, Node >= 22.

## Commands

```bash
npm ci # install (lockfile-respecting)
npm run build # vite build (UMD + ESM, unminified) then minified build
npm test # vitest run β€” unit tests
npm run test:watch # vitest watch
npm run test:e2e # playwright (chromium + firefox + webkit), auto-serves on :5174
npm run release # maintainer-only: bumps version + changelog (.github/scripts/release.sh)
```

CI uses Node 24 (`.github/workflows/ci.yml`), `.nvmrc` says 22, `package.json` engines says `>=22`. Use 22+ locally.

## Repo layout

- `src/core.js` β€” `VanillaValidator` class. The whole engine: rule registration (`addMethod`), locale messages (`addLocaleMessages`), rule resolution from `options.rules` + `data-rule-*` dataset, per-field `element()` validation, `validate()`, `showError`/`clearError`, submit + input event binding.
- `src/index.js` β€” full bundle entry: imports core, all rules, registers all locales, exports `jsValidation` default + `VanillaValidator` named.
- `src/core-entry.js` β€” core-only entry (no rules, no locales). For selective imports via `js-validation/core` + `js-validation/rules/*`.
- `src/rules/*.js` β€” one file per built-in rule. Each calls `VanillaValidator.addMethod(name, validateFn, message)`.
- `src/locales.js` β€” aggregates locale files from `src/locales/<lang>.js` (currently `en`, `es`).
- `test/js-validation.test.js` β€” vitest unit tests (jsdom-free; hand-rolled fake field/form objects).
- `test/selective-import.test.js` β€” verifies core-only entry works.
- `test/e2e/<rule>/` β€” Playwright fixture (`fixture.html`) + spec per rule; `test/e2e/validation.spec.js` is the general one.
- `vite.config.js` (unminified), `vite.config.min.js` (terser-minified), `vite.config.e2e.js` (dev server for e2e, root `test/e2e`, aliases `/src` β†’ `src`).
- `docs-site/` β€” separate Astro docs site (own `package.json`).
- `dist/` β€” build output, gitignored.

## Architecture & conventions

**Rule registration is global/static.** `VanillaValidator.methods` and `.locales` are static class properties. Rules register themselves as a side effect of import β€” `src/rules/email.js` just calls `addMethod` at module top level. Importing a rule file = registering that rule. This is why `core-entry.js` + selective rule imports work, and why test file load order matters (a rule imported by one test file is registered globally for all).

**Adding a built-in rule** (see CONTRIBUTING.md):
1. Create `src/rules/myRule.js` β€” `import { VanillaValidator } from '../core.js'` then `addMethod(name, (value, param, field, validator) => bool, message)`. Message can be a string or `(param, field) => string`.
2. Import it in `src/index.js` (`import './rules/myRule.js'`).
3. Add a locale entry in each `src/locales/<lang>.js`.
4. Add tests in `test/js-validation.test.js` and an e2e fixture/spec in `test/e2e/`.

**Adding a locale:** create `src/locales/<lang>.js` exporting a `{ ruleName: message }` object, then re-export it from `src/locales.js`.

**Rule resolution per field** (`_rulesFor`): merges `data-rule-*` dataset attributes with `options.rules[fieldName]`. Dataset keys like `dataRuleEmail` β†’ rule name `email` (first char after `rule` lowercased). A `data-rule-*` with empty value means `true`; `false`/`'false'` param skips the rule. `range` params are JSON-parsed arrays.

**Message resolution** (`_messageFor`): `options.messages[field][rule]` β†’ locale messages for `this.lang` (default `en`) β†’ English fallback β†’ rule's default message. Functions receive `(param, field)`.

**Validation runs synchronously** in `element()`: iterates rules in insertion order, fails on first invalid rule, shows error, returns false. `validate()` runs `element()` over all non-disabled, named, non-submit fields.

**Error display** toggles an `is-invalid` class on the field and inserts an error element (`<span class="invalid-feedback">`, configurable via options) after the field, keyed by `data-jsv-error-for="<name>"`. The unit tests use fake DOM objects with a manual `classList` Set, so no jsdom is needed.

## Coding conventions

- ESM throughout (`"type": "module"`). No build step for source β€” Vite only bundles `src/index.js` for dist.
- Vanilla JS, no TypeScript, no transpilation. Keep source readable as-is.
- Zero runtime dependencies β€” do not add any. Dev deps only (vite, vitest, playwright, terser).
- Match existing rule file style: single `addMethod` call, message as string or `(param) =>` fn.
- Prototype-pollution hardening: `addMethod`/`addLocaleMessages` reject `__proto__`/`constructor`/`prototype` keys. Preserve this guard when touching registration code.

## Git & PRs

- Branching: `develop` is the working branch (PRs target it); `main` is release-only. Current default for PRs is `develop`.
- PR titles must follow Conventional Commits: `feat|fix|chore|docs|perf|refactor|style|test|config|revert(scope)?!: summary` (enforced by `.github/prlint.json`).
- Dependabot keeps GitHub Actions and dev deps bumped β€” expect frequent `chore(deps)` commits.
- Don't commit `dist/`; it's built in CI and gitignored.
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CLAUDE.md

This file is a thin pointer β€” `AGENTS.md` is the source of truth for agent guidance in this repo.

See [AGENTS.md](AGENTS.md) for the full guide: project overview, commands, repo layout, architecture, coding conventions, and git/PR rules.