velt/process
Write to standard output and read environment variables on the builtin process, with no
import, as in Node:
process.stdout.write(s),process.stderr.write(s): write a string without a newline, ordered withconsole.log/console.error. They returntrue, like Node.process.env.NAME,process.env[name]: the variable asstring | undefined; an unset variable isundefined, as in Node. Each read asks the operating system again, so a check doesn't narrow a second read: read the variable into aconstand test that. Comparing (process.env.MODE === "prod"), switching on it, and testing it withif,!,typeofor?.read the variable as in Node. On Windows names ignore case, as in Node.process.argv: Node's layout,[runtime, script, ...args], soprocess.argv.slice(2)is the arguments. The runtime is the running executable, with symbolic links resolved (veltundervelt dev). The script is the source file undervelt run/velt dev, the test file undervelt test, and the executable again for a built program (as for a Node single-executable application). Under WASI the runtime and the script are the module's path; in the browserprocess.argvis["", ""]. Each read returns a new array, so changing it in place (process.argv.push(x),process.argv[2] = s) is an error: copy it first (const argv = process.argv).process.exit(code: i32),process.memoryUsage()(below).
process is a global, and velt/process exports it too: import { process } from "velt/process" names the same builtin, for code that imports it explicitly as Node code does
(import process from "node:process"; Velt has named exports only, so the import takes braces).
The builtin is known by its name, so it can't be imported under another one
(import { process as p } is an error).
import { args, cwd } from "velt/process" for the rest: command-line arguments, listing and
changing variables, the working directory, byte writes and
standard input.
args(): the arguments only (process.argv.slice(2)).argv(): every argument, starting with the program path (C'sargv).envAll(): Record<string, string>: every variable, as a snapshot (Node'sprocess.envread as an object, soObject.keys(envAll())andObject.entries(envAll())work as in Node). Names are in the order the operating system keeps them, which is Node's order too. On Windows names keep their case and the record's lookups are case-sensitive, unlikeprocess.env.NAME; the per-drive=C:entries are left out, as in Node. In the browser the record is empty.setEnv(name, value),removeEnv(name). Set variables at startup: writes are not synchronized with concurrent reads.cwd(),chdir(path): both throwIoError.exit(code: i32).stdout.write(bytes: u8[]): raw bytes, without a copy for large arrays.stdout.isTTY,stderr.isTTY,stdin.isTTY: whether the stream is a terminal, as Node'sprocess.stdout.isTTY(falsewhen output goes to a file or a pipe, so a CLI can leave out colours).isatty(fd: i32): the same for a file descriptor, as Node'stty.isatty(fd); on Windows only 0, 1 and 2 can be terminals. The answer for a standard stream is computed once, so asking on every write costs nothing. Where Velt differs from Node:isTTYisfalsefor a stream that isn't a terminal, where Node's isundefined.isattyis invelt/process; Node has it innode:tty.- On Windows, a mintty or MSYS terminal (Git Bash) counts as a terminal; in Node it doesn't.
- The builtin
process.stdouthas onlywriteso far: importstdoutforisTTY.
import { stdout } from "velt/process";
const color = stdout.isTTY && process.env.NO_COLOR == null;
console.log(color ? "\u001b[32mok\u001b[0m" : "ok"); // ok, when pipedimport { args, envAll, setEnv, cwd } from "velt/process";
setEnv("GREETING", "hej");
const greeting = process.env.GREETING ?? "hello";
process.stdout.write(`${greeting}, ${args().length} arguments\n`);
console.log(process.env.NO_SUCH_VAR, cwd().length > 0);
console.log(Object.keys(envAll()).includes("GREETING"));
if (process.env.MODE === "prod") console.log("production");Running other programs is in velt/process/child.
Memory usage
process.memoryUsage() is built in (no import), with Node's field names. It returns a
MemoryUsage, { rss: i64; heapUsed: i64 }, in bytes:
rss: the resident set size the OS reports for the process (Linux, macOS and Windows).heapUsed: the memory the allocator has committed for the heap: what it took from the OS, including freed blocks it keeps for reuse, so it does not drop as soon as objects are freed. The runtime doesn't count live bytes, which would slow down every allocation. On Windows it is the process's private committed memory.- On WebAssembly both are the size of the module's linear memory, which only grows.
const m = process.memoryUsage();
const mib = (bytes: i64): string => ((bytes as f64) / 1048576).toFixed(1);
console.log(`rss ${mib(m.rss)} MiB, heap ${mib(m.heapUsed)} MiB`);