https://github.com/stacksjs/pickier
Performant Linter & Formatter.
https://github.com/stacksjs/pickier
css formatter html javascript json jsonc linter md performant stx typescript yaml yml
Last synced: 10 months ago
JSON representation
Performant Linter & Formatter.
- Host: GitHub
- URL: https://github.com/stacksjs/pickier
- Owner: stacksjs
- License: mit
- Created: 2025-07-07T15:02:43.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-08-16T10:10:04.000Z (10 months ago)
- Last Synced: 2025-08-16T10:11:49.940Z (10 months ago)
- Topics: css, formatter, html, javascript, json, jsonc, linter, md, performant, stx, typescript, yaml, yml
- Language: TypeScript
- Homepage: https://pickier.netlify.app
- Size: 577 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Security: .github/SECURITY.md
Awesome Lists containing this project
README

[![npm version][npm-version-src]][npm-version-href]
[![GitHub Actions][github-actions-src]][github-actions-href]
[](http://commitizen.github.io/cz-cli/)
# Pickier
> Fast linting and formatting. Minimal defaults. Extensible. Built for speed.
## Features
- Fast CLI with instant feedback
- Lint and format in one tool
- Zero-config defaults; simple, typed `pickier.config.ts` when you need it
- Import organization: splits type/value imports, sorts modules/specifiers, removes unused named imports
- JSON and config sorting for common files _(e.g. `package.json`, `tsconfig.json`)_
- Flexible formatting: `indent`, `indentStyle` _(tabs or spaces)_, `quotes`, `semi`, `trimTrailingWhitespace`, `maxConsecutiveBlankLines`, `finalNewline`
- Smart whitespace cleanup
- ESLint-style plugin system for lint rules _(load plugins, enable/disable rules, WIP labeling)_
- CI-friendly reporters _(stylish, compact, JSON)_ and strict `--max-warnings` control
- Programmatic API for custom tooling and editor integrations
## Install
```bash
# as a dev dependency
bun add -D pickier
# or
npm i -D pickier
# or
pnpm add -D pickier
# or
yarn add -D pickier
```
You can also run it directly via npx without installing:
```bash
npx pickier --help
# or
bunx pickier --help
```
## Quick start
```bash
# Lint everything, pretty output
pickier lint .
# Auto-fix issues (safe fixes only)
pickier lint . --fix
# Preview fixes without writing
pickier lint . --fix --dry-run --verbose
# Format and write changes
pickier format . --write
# Check formatting without writing (CI-friendly)
pickier format . --check
```
## CLI
- `pickier lint [...globs]`
- `--fix`: apply safe fixes (e.g. remove `debugger` statements)
- `--dry-run`: simulate fixes without writing
- `--max-warnings `: fail if warnings exceed n (default: -1)
- `--reporter `: output format (default: stylish)
- `--ext <.ts,.tsx,.js,...>`: comma-separated extensions (overrides config)
- `--ignore-path `: optional ignore file (e.g. .gitignore)
- `--cache`: reserved (no-op currently)
- `--verbose`
- Examples:
- `pickier lint . --dry-run`
- `pickier lint src --fix`
- `pickier lint "src/**/*.{ts,tsx}" --reporter json`
- `pickier format [...globs]`
- `--write`: write formatted files
- `--check`: only check, non-zero exit on differences
- `--ext <.ts,.tsx,.js,.json,...>`
- `--ignore-path `
- `--verbose`
- Examples:
- `pickier format . --check`
- `pickier format src --write`
- `pickier format "**/*.{ts,tsx,js}" --write`
## Configuration
Pickier works out-of-the-box. To customize, create `pickier.config.ts` in your project root. All fields are optional.
```ts
// pickier.config.ts
import type { PickierConfig } from 'pickier'
const config: PickierConfig = {
verbose: false,
ignores: ['**/node_modules/**', '**/dist/**', '**/build/**'],
lint: {
// which extensions to lint ('.ts' or 'ts' both supported)
extensions: ['ts', 'js'],
// stylish | json | compact
reporter: 'stylish',
// reserved (not used yet)
cache: false,
// -1 disables, otherwise fail when warnings > maxWarnings
maxWarnings: -1,
},
format: {
// which extensions to format
extensions: ['ts', 'js', 'json', 'md', 'yaml', 'yml'],
trimTrailingWhitespace: true,
maxConsecutiveBlankLines: 1,
// one | two | none
finalNewline: 'one',
// 2-space indentation (code files)
indent: 2,
// preferred string quotes in code files: 'single' | 'double'
quotes: 'single',
// when true, safely remove stylistic semicolons
// (never touches for(;;) headers; removes duplicate/empty semicolon statements)
semi: false,
},
rules: {
// 'off' | 'warn' | 'error'
noDebugger: 'error',
noConsole: 'warn',
},
}
export default config
```
### Plugin system (rules)
Pickier supports an ESLint-style plugin system for lint rules. You can load plugins, configure their rules per severity, and mark experimental rules as WIP to surface errors early.
Concepts:
- Plugin: `{ name: string, rules: Record }`
- RuleModule: `{ meta?: { docs?: string; recommended?: boolean; wip?: boolean }, check(content, context) => LintIssue[] }`
- Configure rules via `pluginRules: { 'pluginName/ruleId': 'off' | 'warn' | 'error' | ['warn', options] }`
Define a plugin (example):
```ts
// sample-plugin.ts
import type { PickierPlugin, RuleContext } from 'pickier'
export const samplePlugin: PickierPlugin = {
name: 'sample',
rules: {
'no-todo': {
meta: { docs: 'disallow TODO comments', recommended: true },
check(content: string, ctx: RuleContext) {
const issues = []
const lines = content.split(/\r?\n/)
for (let i = 0; i < lines.length; i++) {
const col = lines[i].indexOf('TODO')
if (col !== -1) {
issues.push({
filePath: ctx.filePath,
line: i + 1,
column: col + 1,
ruleId: 'sample/no-todo',
message: 'Unexpected TODO comment.',
severity: 'warning',
})
}
}
return issues
},
},
'experimental-check': {
meta: { wip: true },
check() {
// not implemented yet
throw new Error('WIP rule')
},
},
},
}
```
Use the plugin in config:
```ts
// pickier.config.ts
import type { PickierConfig } from 'pickier'
import { samplePlugin } from './sample-plugin'
const config: PickierConfig = {
verbose: false,
ignores: ['**/node_modules/**'],
lint: { extensions: ['ts', 'js'], reporter: 'stylish', cache: false, maxWarnings: -1 },
format: { extensions: ['ts', 'js', 'json'], trimTrailingWhitespace: true, maxConsecutiveBlankLines: 1, finalNewline: 'one', indent: 2, quotes: 'single', semi: false },
rules: { noDebugger: 'error', noConsole: 'warn' },
// Register plugins (currently supports in-memory objects)
plugins: [samplePlugin],
// Enable/disable rules and pass options
pluginRules: {
'sample/no-todo': 'warn',
// WIP rules that throw will surface as errors with a :wip-error suffix
'sample/experimental-check': 'error',
},
}
export default config
```
CLI example:
```bash
pickier lint src --reporter json
# If a WIP rule throws, you will see an error like:
# {
# "ruleId": "sample/experimental-check:wip-error",
# "message": "Rule sample/experimental-check is marked as WIP and threw: ...",
# ...
# }
```
### Formatting details
- Semicolons
- Controlled by `format.semi` (default `false`). When `true`, Pickier removes only stylistic semicolons safely:
- preserves `for (init; test; update)` headers
- removes duplicate trailing semicolons (e.g. `foo();;` → `foo();`)
- removes lines that are just empty statements (`;`)
- keeps normal end-of-line semicolons otherwise (non-destructive)
- Imports (TypeScript/JavaScript)
- Groups and rewrites the top import block:
- Splits type-only specifiers into `import type { ... } from 'x'`
- Keeps default and namespace imports
- Removes unused named specifiers only when they have no alias
- Merges multiple imports from the same module
- Sorting
- Order by kind: type imports, side-effect imports, value imports
- For modules: external before relative
- For specifiers: A→Z by identifier; minor normalization for consistent ordering
- Spacing/newlines
- Ensures a single blank line between the import block and the rest of the file
- Respects `format.finalNewline` at EOF
Notes:
- `noDebugger` removes lines that are debugger statements when `--fix` is used.
- `noConsole` controls severity (turn off for libraries that allow console logs).
## Development
This repository contains Pickier’s source under `packages/pickier`.
Common tasks:
```bash
# install deps
bun i
# run tests (with coverage)
bun test --coverage
# build JS and type declarations
bun run -C packages/pickier build
# compile native binary for your platform
bun run -C packages/pickier compile
# compile all platform binaries
bun run -C packages/pickier compile:all
```
Try the CLI locally without publishing:
```bash
# run the TS entry directly
bun packages/pickier/bin/cli.ts --help
# run the built dist CLI
bun packages/pickier/dist/bin/cli.js lint .
# or the compiled native binary (after compile)
./packages/pickier/bin/pickier- --help
```
## Programmatic usage
You can also call Pickier from code (Bun/Node). Useful for custom tooling, editors, or pipelines.
```ts
import type { FormatOptions, LintOptions } from 'pickier'
// example.ts
import { pickierConfig, runFormat, runLint } from 'pickier'
// Lint some directories
const lintOptions: LintOptions = {
fix: true, // apply safe fixes
dryRun: false, // set true to simulate fixes
reporter: 'json', // 'stylish' | 'json' | 'compact'
maxWarnings: 0, // fail on any warning
}
const lintCode = await runLint(['src', 'tests'], lintOptions)
console.log('lint exit code:', lintCode)
// Format some globs
const formatOptions: FormatOptions = {
write: true, // write changes
}
const fmtCode = await runFormat(['src/**/*.ts'], formatOptions)
console.log('format exit code:', fmtCode)
// Access loaded config (from pickier.config.ts or defaults)
console.log('loaded config:', pickierConfig)
```
Run it with Bun:
```bash
bun example.ts
```
## Testing
```bash
bun test
```
## Changelog
Please see our [releases](https://github.com/stacksjs/stacks/releases) page for more information on what has changed recently.
## Contributing
Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
## Community
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
[Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
For casual chit-chat with others using this package:
[Join the Stacks Discord Server](https://discord.gg/stacksjs)
## Postcardware
“Software that is free, but hopes for a postcard.” We love receiving postcards from around the world showing where `pickier` is being used! We showcase them on our website too.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
## Sponsors
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
- [JetBrains](https://www.jetbrains.com/)
- [The Solana Foundation](https://solana.com/)
## Credits
- [Chris Breuer](https://github.com/chrisbbreuer)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/pickier/tree/main/LICENSE.md) for more information.
Made with 💙
[npm-version-src]: https://img.shields.io/npm/v/pickier?style=flat-square
[npm-version-href]: https://npmjs.com/package/pickier
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/pickier/ci.yml?style=flat-square&branch=main
[github-actions-href]: https://github.com/stacksjs/pickier/actions?query=workflow%3Aci