Skip to content
Closed
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
4 changes: 0 additions & 4 deletions .prettierignore

This file was deleted.

6 changes: 0 additions & 6 deletions .prettierrc

This file was deleted.

5 changes: 4 additions & 1 deletion README.md

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"assist": {
"actions": {
"source": {
"organizeImports": "off"
}
}
},
"formatter": {
"indentStyle": "tab",
"lineWidth": 100,
"bracketSpacing": false
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5",
"arrowParentheses": "asNeeded"
}
},
"json": {
"formatter": {"indentStyle": "tab"}
},
"linter": {
"enabled": true,
"includes": ["**", "!bench/**", "!test/manual/**"],
"rules": {
"correctness": {
"preset": "recommended"
},
"suspicious": {
"preset": "recommended",
"noApproximativeNumericConstant": "off",
"noExplicitAny": "off"
},
"style": {
"preset": "recommended",
"useTemplate": "off"
},
"complexity": {
"preset": "recommended"
}
}
},
"overrides": [
{
"includes": ["test/**"],
"linter": {
"rules": {
"correctness": {
"noUnusedVariables": "off",
"noUnusedImports": "off",
"noUnusedFunctionParameters": "off"
},
"suspicious": {
"noImplicitAnyLet": "off"
},
"complexity": {
"useLiteralKeys": "off"
}
}
}
}
],
"files": {
"includes": ["**", "!**/dist", "!**/bin", "!**/node_modules"]
}
}
22 changes: 10 additions & 12 deletions devguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ How we like the code in RexReplace to be.
- **Comments say why, not what.** Write them in plain, simple English. Leave the comment out if the code already says it.
- **No sloppy shortcuts.** If two cases need different calls, write a plain `if`/`else` instead of one call that only half fits both.
- **Everything is printed with `write()` in `src/output.ts`,** so `--nl` knows how each stream ends.
- **The engine never touches the process.** It resolves with the status report of the run, or throws a panic when the run can not go on. Only `src/cli.ts` reads `argv` and stdin, sets the exit code and ends the process.
- **stdout is the result.** That is the names of the affected files, or the content from `--output`, `--output-match` or piped data. Everything else goes to stderr, so the result can always be piped.

## Telling what is going on
Expand All @@ -21,22 +22,19 @@ All output goes through `src/output.ts`. Pick the function by who the message is
| `print(item, delimiter, tail, stream)` | Always | The result. Puts the delimiter after each item, or only between them without a tail. Without a delimiter it is a line break, unless the item already ends with one. | A name, the content of a file, a match |
| `printDone()` | At the end of a run, also when it is stopped | Ends stdout and stderr with a line break when `--nl` is set | |
| `info(name)` | Unless `--quiet` | The name of an affected file. stdout, or stderr when stdout has content. | `a.md` |
| `warn(msg)` | Unless `--quiet` | Something is likely a mistake, but the run goes on | `Data is piped in, but no flag tells what to do with it` |
| `error(msg, data)` | Unless `--quiet-total` | Something went wrong. The run goes on, unless `--halt` is set. | `File not found: a.md` |
| `die(msg, data, help)` | Unless `--quiet-total` | Something went wrong and the run can not go on. Exits with 1. | `No data piped into replacement` |
| `chat(msg)` | `--verbose` (or `--debug`) | The user: what happens to each file | `Open sync: a.md`, `Nothing changed in: a.md` |
| `debug(data)` | `--debug` | The developer: what happens inside, at points that matter | `Work on content from: a.md` |
| `step(data)` | `--verbose` and `--debug` | The developer: a trace of every step, with the data at that step | `Get final regex`, the full config |

- The three levels of telling go from little to a lot: `chat`, then `debug`, then `step`.
- `chat` falls back to `debug` without `--verbose`, so `--debug` alone also shows what happens to each file.
- `debug` and `step` print their data as JSON. That keeps line breaks and spaces visible.
- `chat`, `debug` and `step` only ever write to stderr.
| `warn(msg)` | Unless `--quiet` | Something is likely a mistake, but the run goes on. Also added to `status.warnings`. | `Data is piped in, but no flag tells what to do with it` |
| `error(msg, data)` | Unless `--quiet-total` | Something went wrong. The run goes on, unless `--halt` is set: then a panic is thrown. Also added to `status.errors`, so the CLI gives the exit code 1. | `File not found: a.md` |
| `die(msg, data, help)` | Unless `--quiet-total` | Something went wrong and the run can not go on. Throws a panic, and the CLI exits with 1. Also added to `status.errors`. | `No data piped into replacement` |
| `chat(msg)` | `--verbose`, also with `--quiet`, but not with `--quiet-total` | The user: what happens to each file. Only ever to stderr. Also kept in `engine.steps` of the status report. | `Open sync: a.md`, `Nothing changed in: a.md` |

- What the run did is in the status report: the engine resolves with it, and `--status` prints it. So there is no extra debug output to print along the way.
- `--debug` prints only the status report, with what went into the engine and what it did.
- `warn()`, `error()` and `die()` add to the status report as they go, also when the quiet flags hide what they print.

## Tests

- **Tests are strict, lean and focused too.** One thing per test, and the name of the test says what it is.
- Expect the full result where it matters (`{out, err, exit}`), so nothing extra slips through.
- Expect the full result where it matters (`{out, err, exit}` from `rr()`), so nothing extra slips through. Use `rrStatus()` for the status of the same run.
- A known bug is a `test.todo('BUG #<issue>: ...')` until it is fixed. Then it becomes a real test.
- A test for an issue goes in `test/js/fix/<issue>.test.js`.
- Use Bun native file IO (`Bun.write`, `Bun.file`) and `Bun.spawnSync` in new tests (#734).
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
"test-speed": "bash test/speed/run.sh",
"rebase": "yarn is-git-clean && git fetch && git rebase origin/main",
"load-options": "# rr -h | rr 'Options:(.+)Examples:' _ -ms | rr '\\n {26,}|\\n\\n *' ' ' | rr \"'\" '`' | rr '^ (-.+?), (--[^ ]+) *' '`€1` | **`€2`** ' | rr '(^---- . ----).+?(## Good to know)' '€1 + nl + pipe + nl + nl + €2' readme.md -jspT",
"test-format": "prettier . --list-different || (echo 'Please correct file formatting using `yarn format` and try again.' && exit 1)",
"format": "prettier . --write",
"test-format": "biome format || (echo 'Please correct file formatting using `yarn format` and try again.' && exit 1)",
"format": "biome format --write",
"is-git-clean": "(git diff --quiet --exit-code --cached && git diff --quiet --exit-code) || (echo Please commit or stash changes && exit 1)",
"release": "yarn version",
"prerelease": "yarn is-git-clean && yarn test && yarn test-minify && yarn load-options",
Expand All @@ -60,9 +60,9 @@
"sed"
],
"devDependencies": {
"@biomejs/biome": "2.5.15",
"@types/node": "24.19.1",
"assert": "^2.0.0",
"prettier": "3.9.9",
"yarn": "1.22.22"
},
"resolutions": {
Expand Down
2 changes: 1 addition & 1 deletion src/alias.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// The `€` and `§` aliases are for the shell, where `$` and `\` are hard to type, so the CLI uses them and not the engine.
// Globs, file names and piped data never use them, as `€` and `§` can be part of the text.
export function alias(config) {
const swap = (str) => {
const swap = str => {
if (!str) {
return str;
}
Expand Down
Loading