Everything is done with one command, refingere: it checks a project, and can then run it, test it, compile it, and keep doing so as files change. This page lists its options and explains its output. Getting Started has the download - one file for each platform, with nothing else to install - and walks through the same commands step by step.
Every option is optional, and they can be given in any order. With none at all, it prints its help.
| Option | Does |
|---|---|
<folder or file> | The project: a folder, whose .rfn files and subfolders are one program, or a single file. Relative to the folder the command runs in. Without it, the current folder. |
--check | Checks every file and reports problems. Nothing runs. |
--format, -f | Formats every source file in the project in place - or the one file given - the way Refingere is written: two spaces per level, one blank line between definitions, single spaces between words. Nothing is compiled. Only whitespace ever moves, and a file that doesn't parse is left as it is and named. The same formatter as the editor's - see Formatting. |
--root=<folder or file> | The project, given as an option rather than last: rfn -f --root=shop/main.rfn. |
--expand-in-place[=<file>[:<line>]] | After a clean check, replaces each macro use - every one in the project, a file's, or a line's - in its file by what it stands for, one level deep. What a program macro gives goes to .refingere/expanded. See Expanding a macro in place. |
--edits | With --expand-in-place: write nothing, and print the edits as JSON instead - each with its file, its start and end as a line and a column (both from 1, the column in code points) and its new text; then what each program macro gives, and anything that couldn't be expanded. What an editor runs. |
--run | After a clean check, compiles the project to JavaScript (or WebAssembly, with --wasm) and calls the @main function of the folder. What follows -- is the program's own arguments, &args. |
--test | After a clean check, compiles the project's @test functions that are in no stage, and runs them. --test=e2e,service runs those in the stages it names as well, and --test-all runs every test - see Testing. --test-name=<name> runs just the test a run reports as <name>, whatever its stages - once for each test, since a name can hold commas; it's how an editor runs the one test it's on. With --run too, only the tests run, not @main. |
--errors-only | With --test or --test-all, lists only the tests that fail, then the tally - no line for each one that passes. |
--update-snapshots | With --test or --test-all, a snapshot check whose text differs from the kept one keeps the new text instead of failing - see Snapshot tests. The program runs with REFINGERE_UPDATE_SNAPSHOTS=1 in its environment, which is what turns it on for a program built with --tests too. |
--bench | Compiles the project's @bench functions that are in no stage to time, instead of @main. --bench=slow,e2e times those in the stages it names as well, and --bench-all every one. With --run, times them and measures the memory a call allocates - with --js, on a WebAssembly build of them first - then, when the folder has an @main, runs it once under a profiler and lists the functions it spent its time in. See Benchmarking. |
--profile | Runs @main and the @bench and @test functions in no stage twice - once sampled for where the time goes, once counting every call and every allocation, always compiled to WebAssembly - and writes what it found as a website: .refingere/profile in the project's folder, or profile in --out. --profile=slow,e2e takes those stages' too, --profile-all every one. It runs the program itself, without --run. See Profiling. |
--bench-whole | --bench, timing the project's tests too - each called over and over as an @bench function is, after the benchmarks. The tests are picked by their stages the way --bench=... and --bench-all pick benchmarks; async tests are left out. For a project with tests but no benchmarks yet. |
--watch | Does all of the above, then stays open and does it again whenever a .rfn file under the project changes. Stop it with Ctrl+C. -w is short for it. |
--wasm, --js | Compiles the project to WebAssembly or JavaScript - written to --out, or to standard output without it. With --run or --test, picks what the program is compiled to before it runs. See Compiling to WebAssembly and Compiling to JavaScript. |
--native | Compiles the project to WebAssembly, then that to a native executable with Wasmer - written to --out as program, or named for the project in the current folder. With --run, runs the executable. See Native executables. |
--tests | What --test is made of: compiles the tests instead of @main, without running them - so --tests --wasm --out=out writes a module that runs them. --tests=... and --tests-all pick them as --test=... and --test-all do. |
--out=<folder> | Where results are written instead of printed - the compiled program, and what any of the compiler's steps asked for gave. |
--js-host | With --wasm, compiles the program for a JavaScript host, the way --run and --web do, without running it: its @js functions, a server @serve starts and Gpu reach the host, and program.js.json beside program.wasm says what it needs from it. |
--web | The project is a website: a folder with an index.html whose scripts import Refingere straight from its .rfn files. Serves it on http://localhost:3000, built again on every save, with open pages reloading themselves - a build that fails shows its errors in the page instead. With --compile, builds the finished site into its dist/ folder instead; with --js, its Refingere is compiled to JavaScript rather than WebAssembly. Bun bundles the page. See Websites. |
--port=<port> | Where --web serves the site: rfn --web --port=8080. |
--new | Creates a project from a template, the way bun create does: --new="my-site" names its folder, and --template=website or --template=program picks what it starts as. Either one left out is asked for. The folder has to be new, or empty. |
--progress | Shows what the compiler is doing while it works: one line on stderr - a spinner, the step it's on (Lex, Typecheck, Comptime, ...) and the file - drawn over itself in place. Anything else written clears it first, and it's cleared when the run ends, so only the usual output is left. Before --run starts the program, it's gone too. |
--stats | Once the run's done, shows how long each step took as a table on stderr - apart for the program's own files, the standard library's, and the work on both together (compiling the whole program, running it) - with a total for each. |
--no-color | Shows problems without terminal colours. Setting NO_COLOR does the same. |
--library=<folder> | The standard library programs are compiled with - $REFINGERE_LIBRARY by default, which the downloaded command sets to the copy it unpacks into ~/.cache/refingere (%LOCALAPPDATA%\refingere on Windows) on its first run. |
--help, -h | Lists every option. |
WebAssembly is Refingere's compilation target, chosen web first: it runs in every browser, and the same modules run outside one through WASI - see Design Notes.
With --wasm --out=<folder> the program becomes <folder>/program.wasm - a WASI program that wasmtime, or any other WASI runtime, runs without the compiler. Running it calls @main; built with --tests, it runs every @test and prints the same report as --test. A program that calls JavaScript - its own @js declarations, SQLite, or an @serve server - also gets program.js.json, saying what it needs from its JavaScript host; run it with rfn --wasm --run, which provides one. See JavaScript and TypeScript.
Run with --run, or on Bun, a program has threads: @parallel and Promise.parallel work runs on them, and a --tests build runs its tests side by side, one per core. REFINGERE_THREADS=<n> sets how many, and 0 runs everything on one thread. A WASI runtime has no threads to give, so there the same work runs on the program's own thread, one piece after another - with the same results. See Threads.
&stdout and &stderr are the process's standard output and error, and &logger writes its lines to &stderr. A @main that ends with an error prints it to standard error and exits with status 1. A mistake such as adding a number to text prints error: ... and exits with status 70. A compiled program reads files through the folders it was given: --run gives it the current folder and /; when running a module yourself, pass them to wasmtime with --dir=..
Every builtin type's members come from a standard library compiled into the program with it, and unused memory is reclaimed as the program runs. Values carry their type at runtime rather than having it checked beforehand: every number keeps its own type, so a U8 past 255 is an OverflowError (or wraps or clamps, under +% or +|), and mixing two number types without an as stops the program.
--native takes the WebAssembly one step further, to an executable for the machine it's built on - one file that runs with nothing installed, not Bun, not a WASI runtime. The module is compiled ahead of time to machine code by Wasmer, and linked by the C compiler with Wasmer's runtime into a small launcher, which gives the program what --run does: its arguments, the environment, standard input and output, and the current folder and / to read and write files through.
It needs Wasmer - curl https://get.wasmer.io -sSfL | sh installs it in ~/.wasmer, or $WASMER_DIR says where it is - and a C compiler, cc, or the one $CC names. The executable carries Wasmer's runtime, so even a small program is about 30 MB. Only a plain WASI program can be one: a program that calls JavaScript - its own @js declarations, or an @serve server - needs a JavaScript host, and is refused. The library's own JavaScript, such as Command and SQLite, gives back an error saying so when it's called.
--run and --test compile the program to JavaScript unless --wasm says otherwise, and run it on Bun - the copy inside the downloaded command, so nothing else is needed. --js --out=<folder> writes it as one ES module, <folder>/program.mjs, which Node and Bun run as it is. The program behaves the same either way - it prints the same text, ends with the same errors and exits with the same status - because both are compiled from the same syntax tree, with the same standard library and the same value model: every number keeps its own type, and the exact number arithmetic is the same code.
Run directly, the module calls @main - or, built with --tests, every @test - with the process's arguments as &args. It imports its runtime from the compiler's own copy, so it runs on the machine that compiled it. It reads files through node:fs, relative paths from the current folder. Its calls nest as deep as the JavaScript engine's stack allows - about ten thousand in Node, more in Bun - and deeper recursion stops it with error: stack overflow, as the WebAssembly backend's own limit does.
The compiler works in steps - collecting the files, lexing, parsing, linking names, type checking, lowering to an intermediate language, expanding macros, optimizing and compiling (see The Compiler). Each step has a flag that shows what it made: --files, --tokens, --ast, --analyze, --typecheck, --ir, --optimized-ir and the rest that --help lists. Any of them can be given together; the run goes as far as the last. Their results are printed, or with --out written for each file, and --from=<step> starts from there, reading the step before it back from --out for every file that hasn't changed since.
The output comes in the same order every time:
--run - what it prints, and when @main ended with an error, Program errored: .... --test - a PASS or FAIL line for each test as it finishes, a failure with its error, then how many passed. No errors found. (left out with --run, where the program's own output shows it built), or every problem found, then Program errored: ... saying how many files have errors. When the check finds errors, neither the program nor the tests run - the same way a compiler wouldn't run a program that failed to build.
Each problem is reported with its file, line and column, and the line itself with a ^ under the spot. They're grouped by file, errors first, then warnings - such as a use of something @deprecated, or a @testHelper outside a test - then informational notes. Warnings and notes don't stop the program from running.
The command exits with status 0 when everything it was asked to do worked, and 1 when the check found errors, a test failed, the program ended with an error, or the project couldn't be read. A CI pipeline can run rfn --test to fail the build on any of them.
This has no @main function to compile - --run needs a function marked @main directly in the project's folder, not a folder inside it. no such file '...' - the folder or file doesn't exist, relative to where the command was run. Run it from the folder above the project, or give a full path. No @test functions found. - --test found nothing marked @test in the project. Built from source, the compiler's repository has a second command, written in TypeScript - bun run src/index.ts. It differs in a few options - port= rather than --port=, root= beside --root=, and --target=js for --js; see Building from Source.
It also debugs: --debug builds the program to JavaScript with a source map back to its .rfn files and runs it on Node, waiting for a debugger to attach on port 9229 - VS Code, or Chrome's chrome://inspect - which then stops on breakpoints put on lines of Refingere. --debug=nowait starts at once, --debug=build only builds it and prints where, and with --test it's the tests that run. The VS Code extension's debugger runs it for you - see Editor Support.