{"id":15245,"library":"tinyexec","title":"tinyexec: Minimal Node.js Process Execution","description":"tinyexec is a lightweight Node.js library designed for executing child processes, offering a streamlined, promise-based API as an alternative to Node.js's native `child_process` module or more feature-rich libraries like `execa`. It abstracts away direct stream manipulation, providing a simpler interface for spawning, piping, and awaiting process results. As of its current stable version, 1.1.1, it supports both asynchronous (`x`) and synchronous (`xSync`) command execution, including options for setting timeouts, integrating `AbortSignal` for cancellation, and passing `stdin` input. Key differentiators include its focus on minimalism, automatic resolution of local `node_modules` binaries, and the ability to iterate over process output lines asynchronously. The package is actively maintained with frequent minor releases and bug fixes, and it is ESM-only since version 1.0.0, requiring Node.js 18 or higher.","status":"active","version":"1.1.1","language":"javascript","source_language":"en","source_url":"https://github.com/tinylibs/tinyexec","tags":["javascript","execa","exec","tiny","child_process","spawn","typescript"],"install":[{"cmd":"npm install tinyexec","lang":"bash","label":"npm"},{"cmd":"yarn add tinyexec","lang":"bash","label":"yarn"},{"cmd":"pnpm add tinyexec","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"For parsing complex shell command strings into a command and arguments array when `x` or `xSync` are called with a single string command. This is needed for compatibility with traditional `exec`-like patterns.","package":"args-tokenizer","optional":true}],"imports":[{"note":"The primary asynchronous process execution function. tinyexec is ESM-only since v1.0.0.","wrong":"const x = require('tinyexec');","symbol":"x","correct":"import { x } from 'tinyexec';"},{"note":"Synchronous (blocking) process execution function, introduced in v1.1.0. tinyexec is ESM-only since v1.0.0.","wrong":"const { xSync } = require('tinyexec');","symbol":"xSync","correct":"import { xSync } from 'tinyexec';"},{"note":"TypeScript type definition for the result object returned by `x` and `xSync`, containing `stdout`, `stderr`, `exitCode`, etc.","symbol":"Output","correct":"import type { Output } from 'tinyexec';"}],"quickstart":{"code":"import { x, xSync } from 'tinyexec';\nimport { tokenizeArgs } from 'args-tokenizer'; // Optional, for parsing string commands\n\nasync function runExample() {\n  console.log('--- Async Example (x) ---');\n  try {\n    const result = await x('ls', ['-l', '.'], {\n      timeout: 5000, // Process will be killed after 5 seconds\n      throwOnError: true, // Throws an error if exitCode is non-zero\n      env: { ...process.env, MY_CUSTOM_VAR: 'hello' } // Pass custom environment variables\n    });\n    console.log('stdout:', result.stdout.trim());\n    console.log('stderr:', result.stderr.trim());\n    console.log('exitCode:', result.exitCode);\n  } catch (error) {\n    console.error('Async process failed:', error);\n  }\n\n  console.log('\\n--- Async Iteration Example ---');\n  const proc = x('node', ['-e', 'console.log(\"Line 1\"); await new Promise(r => setTimeout(r, 10)); console.log(\"Line 2\");']);\n  console.log('Output lines:');\n  for await (const line of proc) {\n    console.log(`- ${line.trim()}`);\n  }\n\n  console.log('\\n--- Sync Example (xSync) ---');\n  try {\n    const syncResult = xSync('node', ['-e', 'console.log(\"Hello from sync\"); process.exit(0);']);\n    console.log('sync stdout:', syncResult.stdout.trim());\n  } catch (error) {\n    console.error('Sync process failed:', error);\n  }\n\n  console.log('\\n--- Piping Example ---');\n  // Using args-tokenizer for a more realistic command string parsing example\n  const commandString = 'grep .mjs';\n  const [grepCommand, ...grepArgs] = tokenizeArgs(commandString);\n\n  const proc1 = x('ls', ['.']);\n  const proc2 = proc1.pipe(grepCommand, grepArgs);\n  const pipedResult = await proc2;\n  console.log('Piped .mjs files:', pipedResult.stdout.trim());\n}\n\nrunExample();\n","lang":"typescript","description":"Demonstrates asynchronous (`x`) and synchronous (`xSync`) command execution, including options, piping, and async iteration over output lines. It also shows optional use of `args-tokenizer` for command string parsing."},"warnings":[{"fix":"Migrate your project to use ECMAScript Modules (ESM) by setting `\"type\": \"module\"` in your `package.json` or by using `.mjs` file extensions. Update all `require('tinyexec')` calls to `import { x } from 'tinyexec';`.","message":"tinyexec migrated to an ESM-only build starting with version 1.0.0. CommonJS `require()` statements are no longer supported.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"To ensure consistent error handling, always explicitly set the `throwOnError` option in your calls to `x` or `xSync` (e.g., `{ throwOnError: true }` to throw on non-zero exit codes, or `{ throwOnError: false }` to always return the result object).","message":"The default behavior for `throwOnError` has changed across minor versions. In v0.3.0, `tinyexec` started throwing on non-zero exit codes by default. However, as of recent versions (including 1.x), it *does not* throw by default.","severity":"breaking","affected_versions":">=0.3.0"},{"fix":"If trimmed output is desired, explicitly clean the string: `result.stdout.replace(/\\r?\\n$/, '')`.","message":"Output from `stdout` and `stderr` streams does not automatically trim trailing newlines. The exact raw output is returned.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure your Node.js runtime environment is version 18 or newer. Update Node.js using your preferred package manager (e.g., `nvm install 18` or `nvm use 18`).","message":"tinyexec requires Node.js version 18 or higher due to its ESM-only nature and usage of modern Node.js features.","severity":"gotcha","affected_versions":"<18"},{"fix":"Split the command string into a command and an array of arguments, e.g., `await x('echo', ['Hello, World!'])`. For parsing complex shell syntax, consider using `args-tokenizer`: `const [cmd, ...args] = tokenizeArgs(commandString); await x(cmd, args);`","message":"When attempting to pass commands as a single shell-like string (e.g., `await x('echo \"Hello, World!\"')`), `tinyexec` expects the command and arguments to be split. It does not invoke a shell by default.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Refactor your code to use ES Module `import` syntax: `import { x } from 'tinyexec';`. Ensure your project's `package.json` includes `\"type\": \"module\"` or that your file has a `.mjs` extension.","cause":"Attempting to import `tinyexec` using a CommonJS `require()` statement in a Node.js environment where tinyexec is treated as an ES Module.","error":"Error [ERR_REQUIRE_ESM]: require() of ES Module ... tinyexec/dist/index.js not supported. Instead change the require of index.js in ... to a dynamic import() which is available in all CommonJS modules."},{"fix":"Inspect the `stderr` output (e.g., `error.stderr`) for clues about why the command failed. If a non-zero exit code should not throw an error, set `throwOnError: false` in the options object.","cause":"This error occurs when `throwOnError: true` is set in the options, and the executed process exits with a non-zero status code, indicating a failure.","error":"Error: Command failed with exit code 1"},{"fix":"Chain the `.pipe()` call directly onto the `x()` invocation before awaiting: `const proc1 = x('ls', ['-l']); const proc2 = proc1.pipe('grep', ['.js']); const result = await proc2;`","cause":"Attempting to call `.pipe()` on the `Output` result object after `await x(...)` has resolved, rather than on the live `Promise` returned by `x`.","error":"TypeError: proc.pipe is not a function"}],"ecosystem":"npm"}