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 glob patterns (only for files that exist on the filesystem: ls *.txt)
  • 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

  • Full glob support (mv build/{index.js,index.build.js}, echo {foo,bar}, FOO=a,b echo {$FOO,x})
  • 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

Glob

Glob: object

Type declaration

  • isGlobPattern: function
      • (arg: string): boolean
      • Parameters

        • arg: string

        Returns boolean

  • match: function
      • (pattern: string, options: object): Promise<Array<string>>

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>
  • glob: Glob
  • 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
  • glob: Glob
  • 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 = homedir(), ...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 ?? state.variables[`?`], 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;const 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 cli

cli: Cli<object> = new Cli({binaryLabel: `Yarn Shell`,binaryName: `yarn shell`,binaryVersion: require(`@yarnpkg/shell/package.json`).version || `<unknown>`,})

Const ifNotWin32It

ifNotWin32It: any = isNotWin32? it: it.skip

Const isNotWin32

isNotWin32: boolean = process.platform !== `win32`

Let sigintRefCount

sigintRefCount: number = 0

Functions

applyEnvVariables

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

Const bufferResult

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

cloneState

evaluateArithmetic

  • evaluateArithmetic(arithmetic: ArithmeticExpression, opts: ShellOptions, state: ShellState): Promise<number>

evaluateVariable

  • evaluateVariable(segment: ArgumentSegment & object, opts: ShellOptions, state: ShellState, push: function, pushAndClose?: function): Promise<void>
  • Parameters

    • segment: ArgumentSegment & object
    • opts: ShellOptions
    • state: ShellState
    • push: function
        • (value: string): void
        • Parameters

          • value: string

          Returns void

    • Default value pushAndClose: function = push
        • (value: string): void
        • Parameters

          • value: string

          Returns void

    Returns Promise<void>

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
      • glob: object
        • isGlobPattern: isDynamicPattern
        • match: function
          • match(pattern: string, __namedParameters: object): Promise<object[]> & Promise<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

locateArgsVariableInArithmetic

  • locateArgsVariableInArithmetic(arg: ArithmeticExpression): boolean

locateArgsVariableInSegment

  • locateArgsVariableInSegment(segment: ArgumentSegment | ArithmeticPrimary): boolean

makeActionFromProcedure

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

makeGroupAction

makeProcess

makeSubshellAction

sigintHandler

  • sigintHandler(): void

split

  • split(raw: string): RegExpMatchArray

start

Object literals

Const operators

operators: object

addition

  • addition(left: number, right: number): number

division

  • division(left: number, right: number): number

multiplication

  • multiplication(left: number, right: number): number

subtraction

  • subtraction(left: number, right: number): number

Generated using TypeDoc