Skip to content
Open
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
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
node-version: [18, 20, 22, 24]

steps:
- uses: actions/checkout@v4
Expand All @@ -36,6 +36,15 @@ jobs:
if: matrix.node-version != 18
run: npm run test:coverage

- name: CLI smoke test
run: |
node bin/capstring.js --version
test "$(echo 'hello world' | node bin/capstring.js title)" = "Hello World"
test "$(node bin/capstring.js kebab 'XMLHttpRequest')" = "xml-http-request"

- name: Verify package contents
run: npm pack --dry-run

- name: Upload coverage
uses: codecov/codecov-action@v4
if: matrix.node-version == 20
Expand Down
42 changes: 0 additions & 42 deletions .npmignore

This file was deleted.

59 changes: 34 additions & 25 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,66 @@

## Overview

`capstring` is a lightweight JavaScript library for text capitalization and transformation. Zero dependencies, 24+ styles.
`capstring` is a lightweight JavaScript library for text capitalization and transformation.
Zero dependencies, 37 styles, Unicode and emoji safe, ships a CLI and TypeScript types.

## Quick Start

```javascript
import capstring from 'capstring';
import capstring, { capstringAll } from 'capstring';

capstring('hello world', 'title'); // 'Hello World'
capstring('hello world', 'camel'); // 'helloWorld'
capstring('hello world', 'kebab'); // 'hello-world'
capstring('hello world', 'constant'); // 'HELLO_WORLD'
capstring('hello world', 'title'); // 'Hello World'
capstring('XMLHttpRequest', 'kebab'); // 'xml-http-request'
capstring('Crème Brûlée', 'slug'); // 'creme-brulee'
capstringAll('hi').upper; // 'HI'
```

## API
```bash
npx capstring kebab "Hello World" # hello-world
echo hi | npx capstring --all # every style
```

### Main Function
## API

```javascript
capstring(str, style)
capstring(str, style = 'same', { strict = false } = {})
```

- `str` - String to transform
- `style` - Style name (default: 'same')
- Returns: Transformed string, or `false` if input invalid

### Helper Functions
- `strict` - Throw `TypeError` / `RangeError` instead of returning `false` / the input
- Returns: Transformed string; `''` for empty input; `false` if `str` is not a string

```javascript
import { getStyles, isValidStyle, STYLES } from 'capstring';
import { capstringAll, getStyles, isValidStyle, STYLES, CATEGORIES } from 'capstring';

getStyles(); // ['same', 'none', 'proper', ...]
isValidStyle('kebab'); // true
STYLES; // Frozen array of all style names
capstringAll('hi'); // { same: 'hi', none: '', ... } in STYLES order
getStyles(); // fresh copy of STYLES
isValidStyle('kebab'); // true
STYLES; // frozen array of all 37 style names
CATEGORIES; // frozen { case, code, fun, encoding, art }
```

## All 24 Styles
## All 37 Styles

| Category | Styles |
|----------|--------|
| Case | same, none, proper, title, sentence, upper, lower, swap |
| Code | camel, pascal, snake, kebab, slug, constant, python, dot, path |
| Fun | leet, reverse, sponge, mock, alternate, crazy, random |
| case | same, none, proper, title, sentence, upper, lower, swap |
| code | camel, pascal, snake, kebab, slug, constant, python, dot, path, train, hashtag, acronym |
| fun | reverse, sponge, mock, alternate, crazy, random, clap, piglatin |
| encoding | leet, rot13, morse, binary |
| art | flip, smallcaps, bubble, wide, strike |

## Files

- `index.js` - the whole library. Must stay browser-safe: no `node:` imports, no `process`.
- `cli.js` - CLI logic as `main(argv, io)`; `bin/capstring.js` is the executable shim.
- `index.d.ts` - hand-written types. `test/types.test.js` fails if the `Style` union drifts from `STYLES`.

## Testing

```bash
npm test # Run tests
npm run lint # Run linter
npm run test:coverage # Coverage report
npm run test:coverage # Coverage report (100% thresholds enforced)
```

## Related

- **cAPIta** - REST API for capstring (https://github.com/brianfunk/cAPIta)
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2026-10-02

### Added

- **8 new styles** (37 total): `smallcaps`, `bubble`, `wide`, `strike`, `clap`, `morse`, `binary`, `piglatin`
- **CLI**: `npx capstring <style> [text]`, `--all`, `--list`, `--json`, `--help`, `--version`; reads stdin when no text is given
- **TypeScript declarations** (`index.d.ts`) with a `Style` union type for autocomplete
- `capstringAll(str)` - every style at once, keyed by style name
- `CATEGORIES` - frozen style groupings (`case`, `code`, `fun`, `encoding`, `art`)
- `{ strict: true }` option - throw `TypeError` / `RangeError` instead of returning `false` / the input
- Smart tokenizer for code styles: splits on camelCase boundaries, `_`, `-`, `.`, `/` and punctuation (`XMLHttpRequest` → `xml-http-request`)
- `files` whitelist, `sideEffects: false`, `./package.json` export, `prepublishOnly` gate
- 100% coverage thresholds enforced; Node 24 added to CI; CLI smoke test in CI

### Behavior changes

- Empty string input now returns `''` instead of `false` (non-string input still returns `false`)
- `slug` is now a real slugifier: diacritics folded, punctuation removed, ASCII only (`Crème Brûlée & Co.` → `creme-brulee-co`). It no longer equals `kebab`, which keeps Unicode letters
- All code styles tokenize camelCase and separator input (`helloWorld` → `hello_world`, was `helloworld`)
- `title` is Unicode aware (`élan vital` → `Élan Vital`, was `éLan Vital`); `_` now breaks words
- `sentence` capitalizes after `.`, `!`, `?` (`hello. world` → `Hello. World`)
- `leet` uses the conventional ASCII map and preserves case (`hello WORLD` → `h3110 W0R1D`, was `h3££0 w0r£d`)
- `alternate` counts Unicode letters, not just a-z
- `hashtag` of text with no words returns `''` instead of `#`
- `crazy` output may differ from 1.0.0 for text containing emoji (now seeded per code point)

### Fixed

- `reverse` and `flip` no longer corrupt emoji, flags, or combining accents (grapheme-cluster aware)
- All styles iterate code points instead of UTF-16 code units
- `@eslint/js` declared as a devDependency

### Docs

- Style count corrected everywhere (README, AGENTS.md, package.json)
- README regrouped into Case / Code / Fun / Encodings / Unicode Art with behavior notes

## [1.0.0] - 2026-02-01

### Breaking Changes
Expand Down
33 changes: 20 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Context

capstring is a lightweight JavaScript library for text capitalization and transformation. It supports 29 different styles including case transformations, code conventions, and fun styles.
capstring is a lightweight JavaScript library for text capitalization and transformation. It supports 37 styles including case transformations, code conventions, encodings, and fun Unicode styles. Serious and robust, even though it's for fun.

## Development Commands

Expand All @@ -22,26 +22,33 @@ npm run test:coverage # Run tests with coverage report

## Architecture

Single-file library with a main function and helper exports:
- `capstring(str, style)` - Main transformation function
- `getStyles()` - Returns array of all style names
- `isValidStyle(style)` - Validates a style name
- `STYLES` - Frozen array constant of all styles
Single-file library plus a thin CLI:
- `index.js` - the whole library. **Must stay browser-safe**: no `node:` imports, no `process`.
- `capstring(str, style, { strict })` - Main transformation function
- `capstringAll(str, options)` - Every style at once
- `getStyles()`, `isValidStyle(style)`, `STYLES`, `CATEGORIES`
- `cli.js` - `main(argv, io)` with injected I/O so it is unit-testable; `bin/capstring.js` is the shim
- `index.d.ts` - hand-written types; `test/types.test.js` enforces that the `Style` union matches `STYLES`

## Supported Styles (29 total)
## Supported Styles (37 total)

**Case:** same, none, proper, title, sentence, upper, lower, swap
**Code:** camel, pascal, snake, kebab, slug, constant, python, dot, path, train
**Fun:** leet, reverse, sponge, mock, alternate, crazy, random
**New:** hashtag, acronym, rot13, flip
**Code:** camel, pascal, snake, kebab, slug, constant, python, dot, path, train, hashtag, acronym
**Fun:** reverse, sponge, mock, alternate, crazy, random, clap, piglatin
**Encoding:** leet, rot13, morse, binary
**Art:** flip, smallcaps, bubble, wide, strike

Never remove a style name; `proper` and `python` are kept as aliases on purpose.

## When Making Changes

1. Ensure all tests pass: `npm test`
2. Maintain 100% coverage: `npm run test:coverage`
2. Maintain 100% coverage (thresholds enforced): `npm run test:coverage`
3. Run linter: `npm run lint`
4. Update CHANGELOG.md for any user-facing changes
5. Preserve the ASCII art header
4. Update CHANGELOG.md for any user-facing changes; behavior changes get their own section
5. Adding a style: add to `STYLES` **and** one `CATEGORIES` group **and** the `Style` union in `index.d.ts`, plus README table
6. Preserve the ASCII art header
7. Verify `npm pack --dry-run` still lists only the intended files

## Related Projects

Expand Down
Loading
Loading