Options
All
  • Public
  • Public/Protected
  • All
Menu

Package yarnpkg-shell

@yarnpkg/shell

A JavaScript implementation of a bash-like shell (we use it in Yarn 2 to provide cross-platform scripting). This package exposes an API that abstracts both the parser and the interpreter; should you only need the parser you can check out @yarnpkg/parsers, but you probably won't need it.

Usage

import {execute} from '@yarnpkg/shell';

process.exitCode = await execute(ls "$1" | wc -l, [process.cwd()]);

Features

  • Typechecked
  • Portable across systems
  • Supports custom JS builtins
  • Supports pipes
  • Supports logical operators
  • Supports subshells
  • Supports variables
  • Supports string manipulators
  • Supports argc/argv
  • Supports the most classic builtins
  • Doesn't necessarily need to access the fs

Help Wanted

  • Glob support (ls *.txt)
  • More string manipulators

Non-Goals

  • Perfect POSIX compliance (basic scripting is enough for now)
  • Multiline scripts (we mostly target one-liners)
  • Control structures (same reason)

Type aliases

ProcessImplementation

ProcessImplementation: function

Type declaration

    • Parameters

      Returns object

      • promise: Promise<number>
      • stdin: Writable

ShellBuiltin

ShellBuiltin: function

Type declaration

ShellOptions

ShellOptions: object

Type declaration

  • args: Array<string>
  • builtins: Map<string, ShellBuiltin>
  • initialStderr: Writable
  • initialStdin: Readable
  • initialStdout: Writable

ShellState

ShellState: object

Type declaration

  • cwd: PortablePath
  • environment: object
    • [key: string]: string
  • exitCode: number | null
  • procedures: object
  • stderr: Writable
  • stdin: Readable
  • stdout: Writable
  • variables: object
    • [key: string]: string

StartOptions

StartOptions: object

Type declaration

Stdio

Stdio: [any, any, any]

UserOptions

UserOptions: object

Type declaration

  • builtins: object
  • cwd: PortablePath
  • env: object
    • [key: string]: string | undefined
  • stderr: Writable
  • stdin: Readable | null
  • stdout: Writable
  • variables: object
    • [key: string]: string

Variables

Const BUILTINS

BUILTINS: Map<string, function> = new Map<string, ShellBuiltin>([[`cd`, async ([target, ...rest]: Array<string>, opts: ShellOptions, state: ShellState) => {const resolvedTarget = ppath.resolve(state.cwd, npath.toPortablePath(target));const stat = await xfs.statPromise(resolvedTarget);if (!stat.isDirectory()) {state.stderr.write(`cd: not a directory\n`);return 1;} else {state.cwd = resolvedTarget;return 0;}}],[`pwd`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {state.stdout.write(`${npath.fromPortablePath(state.cwd)}\n`);return 0;}],[`true`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {return 0;}],[`false`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {return 1;}],[`exit`, async ([code, ...rest]: Array<string>, opts: ShellOptions, state: ShellState) => {return state.exitCode = parseInt(code, 10);}],[`echo`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {state.stdout.write(`${args.join(` `)}\n`);return 0;}],[`__ysh_run_procedure`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {const procedure = state.procedures[args[0]];const exitCode = await start(procedure, {stdin: new ProtectedStream<Readable>(state.stdin),stdout: new ProtectedStream<Writable>(state.stdout),stderr: new ProtectedStream<Writable>(state.stderr),}).run();return exitCode;}],[`__ysh_set_redirects`, async (args: Array<string>, opts: ShellOptions, state: ShellState) => {let stdin = state.stdin;let stdout = state.stdout;let stderr = state.stderr;const inputs: Array<() => Readable> = [];const outputs: Array<Writable> = [];let t = 0;while (args[t] !== `--`) {const type = args[t++];const count = Number(args[t++]);const last = t + count;for (let u = t; u < last; ++t, ++u) {switch (type) {case `<`: {inputs.push(() => {return xfs.createReadStream(ppath.resolve(state.cwd, npath.toPortablePath(args[u])));});} break;case `<<<`: {inputs.push(() => {const input = new PassThrough();process.nextTick(() => {input.write(`${args[u]}\n`);input.end();});return input;});} break;case `>`: {outputs.push(xfs.createWriteStream(ppath.resolve(state.cwd, npath.toPortablePath(args[u]))));} break;case `>>`: {outputs.push(xfs.createWriteStream(ppath.resolve(state.cwd, npath.toPortablePath(args[u])), {flags: `a`}));} break;}}}if (inputs.length > 0) {const pipe = new PassThrough();stdin = pipe;const bindInput = (n: number) => {if (n === inputs.length) {pipe.end();} else {const input = inputs[n]();input.pipe(pipe, {end: false});input.on(`end`, () => {bindInput(n + 1);});}};bindInput(0);}if (outputs.length > 0) {const pipe = new PassThrough();stdout = pipe;for (const output of outputs) {pipe.pipe(output);}}const exitCode = await start(makeCommandAction(args.slice(t + 1), opts, state), {stdin: new ProtectedStream<Readable>(stdin),stdout: new ProtectedStream<Writable>(stdout),stderr: new ProtectedStream<Writable>(stderr),}).run();// Close all the outputs (since the shell never closes the output stream)await Promise.all(outputs.map(output => {// Wait until the output got flushed to the diskreturn new Promise(resolve => {output.on(`close`, () => {resolve();});output.end();});}));return exitCode;}],])

Const ifNotWin32It

ifNotWin32It: any = process.platform !== `win32`? it: it.skip

Functions

applyEnvVariables

  • applyEnvVariables(environmentSegments: Array<EnvSegment>, opts: ShellOptions, state: ShellState): Promise<object>

Const bufferResult

  • bufferResult(command: string, args?: Array<string>, options?: any): Promise<object>

cloneState

execute

  • execute(command: string, args?: Array<string>, __namedParameters?: object): Promise<number>
  • Parameters

    • command: string
    • Default value args: Array<string> = []
    • Default value __namedParameters: object = {}
      • builtins: object
      • cwd: string & object
      • env: object
        • [key: string]: string | undefined
      • stderr: Writable
      • stdin: null | Readable
      • stdout: Writable
      • variables: object
        • [key: string]: string

    Returns Promise<number>

executeBufferedSubshell

executeCommandChain

executeCommandLine

executeShellLine

interpolateArguments

  • interpolateArguments(commandArgs: Array<Argument>, opts: ShellOptions, state: ShellState): Promise<string[]>

locateArgsVariable

  • locateArgsVariable(node: ShellLine): boolean

locateArgsVariableInArgument

  • locateArgsVariableInArgument(arg: Argument): boolean

locateArgsVariableInSegment

  • locateArgsVariableInSegment(segment: ArgumentSegment): boolean

makeBuiltin

makeCommandAction

  • Executes a command chain. A command chain is a list of commands linked together thanks to the use of either of the | or |& operators:

    $ cat hello | grep world | grep -v foobar

    $ cat hello | grep world | grep -v foobar

    Parameters

    Returns function

      • Parameters

        Returns object

        • promise: Promise<number>
        • stdin: Writable

makeProcess

makeSubshellAction

nextTick

  • nextTick(): Promise<unknown>

start

Generated using TypeDoc