From 0643d25ca39e348f97e2cc9fcd24d7f958d7cbe2 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Fri, 20 Jun 2025 16:29:13 -0600 Subject: [PATCH] claude.md --- .github/workflows/main.yml | 1 + CLAUDE.md | 48 ++++++++++++++++++ benchmark.js | 99 ++++++++++++++++++++------------------ package.json | 4 +- 4 files changed, 104 insertions(+), 48 deletions(-) create mode 100644 CLAUDE.md diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d588995..5ed650d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,6 +2,7 @@ name: CI on: - push - pull_request + - workflow_dispatch jobs: test: name: Node.js ${{ matrix.node-version }} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..75365a2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,48 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +**Testing:** +- `npm test` - Run all tests (linting, unit tests, coverage, TypeScript definitions) +- `npx ava test/.js` - Run a specific test file +- `npx xo` - Run linting only +- `npx tsd` - Run TypeScript definition tests only + +**Benchmarking:** +- `npm run bench` - Run performance benchmarks + +## Architecture + +Chalk is a zero-dependency terminal string styling library built as a pure ESM module. Key architectural elements: + +1. **Core Files:** + - `source/index.js` - Main entry point with the chalk factory and chainable API implementation + - `source/utilities.js` - Helper functions for string manipulation and color conversion + - `source/vendor/` - Vendored dependencies (ansi-styles and supports-color) to avoid external dependencies + +2. **Internal Symbols:** + - `GENERATOR` - Manages chalk instance creation and configuration + - `STYLER` - Handles style application and chaining + - `IS_EMPTY` - Tracks empty string optimization + +3. **API Design:** + - Uses ES6 Proxy for dynamic property access (e.g., `chalk.red.bold`) + - Chainable API with lazy property definition for performance + - Supports nested styles with proper ANSI escape code management + - Template literal support via tagged template syntax + +4. **Color Level Detection:** + - Automatically detects terminal color support (levels 0-3) + - Separate instances for stdout and stderr with independent color levels + - Can be overridden via `new Chalk({level: n})` or `FORCE_COLOR` environment variable + +## Important Notes + +- This is a pure ESM package (v5+) - use `import` not `require` +- Maintain zero production dependencies - vendor any required modules +- All style properties are dynamically generated via Proxy - there's no hardcoded list +- Performance is critical - run benchmarks before/after changes to core functionality +- The codebase uses symbols for private properties to prevent collisions +- Template literal processing has special handling for newlines and empty strings \ No newline at end of file diff --git a/benchmark.js b/benchmark.js index c5c6e27..7dfdfd0 100644 --- a/benchmark.js +++ b/benchmark.js @@ -1,57 +1,64 @@ -/* globals suite, bench */ -import chalk from './index.js'; +import { Bench } from "tinybench"; +import chalk from "./source/index.js"; -suite('chalk', () => { - const chalkRed = chalk.red; - const chalkBgRed = chalk.bgRed; - const chalkBlueBgRed = chalk.blue.bgRed; - const chalkBlueBgRedBold = chalk.blue.bgRed.bold; +const bench = new Bench({ time: 150 }); - const blueStyledString = 'the fox jumps' + chalk.blue('over the lazy dog') + '!'; +const chalkRed = chalk.red; +const chalkBgRed = chalk.bgRed; +const chalkBlueBgRed = chalk.blue.bgRed; +const chalkBlueBgRedBold = chalk.blue.bgRed.bold; - bench('1 style', () => { - chalk.red('the fox jumps over the lazy dog'); - }); +const blueStyledString = + "the fox jumps" + chalk.blue("over the lazy dog") + "!"; - bench('2 styles', () => { - chalk.blue.bgRed('the fox jumps over the lazy dog'); - }); - - bench('3 styles', () => { - chalk.blue.bgRed.bold('the fox jumps over the lazy dog'); - }); - - bench('cached: 1 style', () => { - chalkRed('the fox jumps over the lazy dog'); - }); - - bench('cached: 2 styles', () => { - chalkBlueBgRed('the fox jumps over the lazy dog'); - }); - - bench('cached: 3 styles', () => { - chalkBlueBgRedBold('the fox jumps over the lazy dog'); - }); - - bench('cached: 1 style with newline', () => { - chalkRed('the fox jumps\nover the lazy dog'); - }); - - bench('cached: 1 style nested intersecting', () => { +bench + .add("1 style", () => { + chalk.red("the fox jumps over the lazy dog"); + }) + .add("2 styles", () => { + chalk.blue.bgRed("the fox jumps over the lazy dog"); + }) + .add("3 styles", () => { + chalk.blue.bgRed.bold("the fox jumps over the lazy dog"); + }) + .add("cached: 1 style", () => { + chalkRed("the fox jumps over the lazy dog"); + }) + .add("cached: 2 styles", () => { + chalkBlueBgRed("the fox jumps over the lazy dog"); + }) + .add("cached: 3 styles", () => { + chalkBlueBgRedBold("the fox jumps over the lazy dog"); + }) + .add("cached: 1 style with newline", () => { + chalkRed("the fox jumps\nover the lazy dog"); + }) + .add("cached: 1 style nested intersecting", () => { chalkRed(blueStyledString); - }); - - bench('cached: 1 style nested non-intersecting', () => { + }) + .add("cached: 1 style nested non-intersecting", () => { chalkBgRed(blueStyledString); - }); - - bench('cached: 1 style template literal', () => { + }) + .add("cached: 1 style template literal", () => { // eslint-disable-next-line no-unused-expressions chalkRed`the fox jumps over the lazy dog`; - }); - - bench('cached: nested styles template literal', () => { + }) + .add("cached: nested styles template literal", () => { // eslint-disable-next-line no-unused-expressions chalkRed`the fox {bold jumps} over the {underline lazy} dog`; }); -}); + +await bench.run(); + +// Custom table output without samples, latency med, or throughput med +const customTable = bench.tasks.map((task) => ({ + "Task name": task.name, + // 'Latency avg (ns)': task.result.latency.mean.toFixed(2) + ' ± ' + task.result.latency.rme.toFixed(2) + '%', + "Throughput avg (ops/s)": + task.result.throughput.mean.toFixed(0) + + " ± " + + task.result.throughput.rme.toFixed(2) + + "%", +})); + +console.table(customTable); diff --git a/package.json b/package.json index 23b4ce3..b4e133c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "scripts": { - "test": "xo && c8 ava && tsd", + "test": "c8 ava && tsd", "bench": "matcha benchmark.js" }, "files": [ @@ -57,7 +57,7 @@ "color-convert": "^2.0.1", "execa": "^6.0.0", "log-update": "^5.0.0", - "matcha": "^0.7.0", + "tinybench": "^4.0.1", "tsd": "^0.19.0", "xo": "^0.57.0", "yoctodelay": "^2.0.0"