A tool for writing better scripts

Overview

🐚 zx

#!/usr/bin/env zx

await $`cat package.json | grep name`

let branch = await $`git branch --show-current`
await $`dep deploy --branch=${branch}`

await Promise.all([
  $`sleep 1; echo 1`,
  $`sleep 2; echo 2`,
  $`sleep 3; echo 3`,
])

let name = 'foo bar'
await $`mkdir /tmp/${name}`

Bash is great, but when it comes to writing scripts, people usually choose a more convenient programming language. JavaScript is a perfect choice, but standard Node.js library requires additional hassle before using. zx package provides useful wrappers around child_process, escapes arguments and gives sensible defaults.

Install

npm i -g zx

Documentation

Write your scripts in a file with .mjs extension in order to be able to use await on top level. If you prefer .js extension, wrap your script in something like void async function () {...}().

Add next shebang at the beginning of your script:

#!/usr/bin/env zx

Now you will be able to run your script as:

chmod +x ./script.mjs
./script.mjs

Or via zx bin:

zx ./script.mjs

When using zx via bin or shebang, all $, cd, fetch, etc are available without imports.

$`command`

Executes given string using exec function from child_process package and returns Promise.

let count = parseInt(await $`ls -1 | wc -l`)
console.log(`Files count: ${count}`)

Example. Upload files in parallel:

let hosts = [...]
await Promise.all(hosts.map(host =>
  $`rsync -azP ./src ${host}:/var/www`  
))

If executed program returns non-zero exit code, ProcessOutput will be thrown.

try {
  await $`exit 1`
} catch (p) {
  console.log(`Exit code: ${p.exitCode}`)
  console.log(`Error: ${p.stderr}`)
}

ProcessOutput

class ProcessOutput {
  readonly exitCode: number
  readonly stdout: string
  readonly stderr: string
  toString(): string
}

cd()

Changes working directory.

cd('/tmp')
await $`pwd` // outputs /tmp 

fetch()

This is a wrapper around node-fetch package.

let resp = await fetch('http://wttr.in')
if (resp.ok) {
  console.log(await resp.text())
}

question()

This is a wrapper around readline package.

type QuestionOptions = { choices: string[] }

function question(query: string, options?: QuestionOptions): Promise<string>

Usage:

let username = await question('What is your username? ')
let token = await question('Choose env variable: ', {
  choices: Object.keys(process.env)
})

chalk package

The chalk package is available without importing inside scripts.

console.log(chalk.blue('Hello world!'))

fs package

The fs package is available without importing inside scripts.

let content = await fs.readFile('./package.json')

Promisified version imported by default. Same as if you write:

import {promises as fs} from 'fs'

os package

The os package is available without importing inside scripts.

await $`cd ${os.homedir()} && mkdir example`

$.shell

Specifies what shell is used. Default is which bash.

$.shell = '/usr/bin/bash'

$.prefix

Specifies command what will be added to all command.

Default is set -euo pipefail;.

$.quote

Specifies a function what will be used for escaping special characters in command substitution.

Default is shq package.

$.verbose

Specifies verbosity. Default: true.

In verbose mode prints executed commands with outputs of it. Same as set -x in bash.

Importing from other scripts

It's possible to use $ and others with explicit import.

#!/usr/bin/env node
import {$} from 'zx'
await $`date`

Passing env variables

process.env.FOO = 'bar'
await $`echo $FOO`

Executing remote scripts

If arg to zx bin starts with https://, it will be downloaded and executed.

zx https://medv.io/example-script.mjs

License

Apache-2.0

Disclaimer: This is not an officially supported Google product.

Comments
  • $'s template strings broken with zx version 2

    $'s template strings broken with zx version 2

    Hello! I noticed with version 2 most of my scripts broke. This is caused by this new function that adds a dollar and a quote in some specific conditions.

    I don't really understood the point of it so I prefer asking, is it an intended behavior? If so it would be great to add an explanation on the Readme and a note in a changelog documentation. If not would be glad to correct it :)

    Expected Behavior

    echo '
    
    const today = new Date()
    $`echo "Today year is ${today.getFullYear()} and full date is ${today.toISOString()}"`
    
    ' > test.mjs
    zx test.mjs
    
    >>> Today year is 2021 and full date is 2021-07-15T16:18:35.288Z
    

    Actual Behavior

    echo '
    
    const today = new Date()
    $`echo "Today year is ${today.getFullYear()} and full date is ${today.toISOString()}"`
    
    ' > test.mjs
    zx test.mjs
    
    >>> Today year is 2021 and full date is $'2021-07-15T16:18:35.288Z'
    

    Steps to Reproduce the Problem

    1. npm i -g zx
    2. Execute the lines I pasted before

    Specifications

    Works as expected with zx version 1.14.1

    Broken on :

    • Version: 2.0.0
    • Platform: wsl2

    Thanks for your help!

    opened by apibrac 34
  • $verbose = false is not displaying command output. verbose should be just for the command

    $verbose = false is not displaying command output. verbose should be just for the command

    Works for me.

    ⌘ zx βŽ‡ main ❯❯❯ cat foo.mjs
    #!/usr/local/bin/node
    import { $ } from './index.mjs'
    $.verbose = false
    await ($`uptime`)
    ⌘ zx βŽ‡ main ❯❯❯ node foo.mjs
    ⌘ zx βŽ‡ main ❯❯❯
    

    Originally posted by @antonmedv in https://github.com/google/zx/issues/80#issuecomment-841612690

    opened by bsnux 33
  • [ERR_PACKAGE_PATH_NOT_EXPORTED]: No

    [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in package.json

    Nbb, a ClojureScript interpreter for Node.js uses dynamic import to load libraries. To resolve which JS file to load, it uses code like the following:

    import { createRequire } from 'module';
    
    const req = createRequire(import.meta.url);
    
    console.log(req.resolve('zx'));
    

    This code worked in zx 5.3.0 but stopped working in 6.0.7 with the following error:

    $ node script.mjs
    path
    node:internal/errors:465
        ErrorCaptureStackTrace(err);
        ^
    
    Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /private/tmp/test-nbb/node_modules/zx/package.json
        at new NodeError (node:internal/errors:372:5)
        at throwExportsNotFound (node:internal/modules/esm/resolve:433:9)
        at packageExportsResolve (node:internal/modules/esm/resolve:657:7)
        at resolveExports (node:internal/modules/cjs/loader:482:36)
        at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
        at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
        at Function.resolve (node:internal/modules/cjs/helpers:108:19)
        at file:///private/tmp/test-nbb/script.mjs:6:17
        at ModuleJob.run (node:internal/modules/esm/module_job:197:25)
        at async Promise.all (index 0) {
      code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
    }
    
    opened by borkdude 29
  • Adds $ sign

    Adds $ sign

    In the version 7.0.5 of zx there is an issue with escaping bash variables when there is a js var with a : character in it. This bug introduced in zx 7.0.4. when running version 7.0.3 there is no issue when going to 7.0.4 there is an issue

    Expected Behavior

    expected output: special/var:iable actual output: special/var:iable

    Actual Behavior

    expected output: special/var:iable actual ouput: $special/var:iable

    whats wrong: the $ sign should not be there

    Steps to Reproduce the Problem

    • create a file test.mjs
    • add follow script
    const variable  ="special/var:iable"
    await $`echo ${variable}`;
    

    see repo https://github.com/geoffreysamper/zx-issue-7.05-escaping-vars

    Specifications

    • Version: 7.0.5
    • Platform: ubuntu 22.04
    opened by geoffreysamper 28
  • More granular verbosity

    More granular verbosity

    Current Behaviour

    When setting $.verbose = false; to hide the command that's going to be run, it also hides the command's output.

    Expected Behaviour

    I would like a way to not print the command that's going to be run, but still show the command's output.

    Steps to reproduce

    $.shell = bash;, because the example uses select which is bash-only.

    const listOfChoice = ['a', 'b', 'c'];
    await $`select item in ${listOfChoice}
    do
      echo $item
      break;
    done`;
    

    $.verbose = true;

    $ select item in a b c
    > do
    >   echo $item
    >   break;
    > done
    1) a
    2) b
    3) c
    #?
    

    $.verbose = false;

    Will output nothing, but the shell will be waiting for a user input.

    Suggestions

    Just brainstorming here

    1. add a $.verboseCommand, or
    2. add a $.hideOutput configuration, or
    3. change the type of $.verbose (this would be backward-compatible, and extensible):
    type VerboseComponent = 'commands' | 'stdout' | 'stderr';
    type Verbose = boolean | VerboseComponent | VerboseComponent[];
    
    // example usage:
    const verboseShowAll: Verbose = true;
    const verboseShowAll2: Verbose = [ 'commands', 'stdout', 'stderr' ];
    const verboseShowCommandsButNoOutput: Verbose = 'commands';
    const verboseShowStderrOnly: Verbose = 'stderr';
    const verboseShowOutputButNotCommand: Verbose = ['stderr', 'stdout' ];
    const verboseShowNothing: Verbose = false;
    

    Specifications

    • Version: 5.1.0
    • Platform: any
    feature 
    opened by fstamour 23
  • refactor: introduce internal bound context

    refactor: introduce internal bound context

    Introduces bound context to pass it down to helpers (verbose, quiet, timeout, etc).

    Later we can add smth like:

    $.o({spawn: customSpawn, quoute: v => v, nothrow: true})`cmd ${arg}`
    $.quiet = $.o({verbose: false})
    
    • [x] Tests pass
    opened by antongolub 22
  • Publish sync CJS entrypoint to allow ts-node without --loader

    Publish sync CJS entrypoint to allow ts-node without --loader

    Is it acceptable for zx to publish a CommonJS entrypoint in addition to the ESM entrypoint? This would be a big quality-of-life win for anyone combining zx with ts-node, because they would not need to use --loader nor to asynchronously load zx within their .ts scripts.

    The only use of top-level await within zx is a call to which. which has a sync alternative that can be used to avoid top-level await.

    We can still keep both the ESM and CommonJS entrypoints, so consumers can use whichever they prefer.

    I'd be happy to send a pull request for this if it will be accepted.

    Expected Behavior

    Ability to import zx from CommonJS without await, so that ts-node ./script.ts works without needing --loader

    Actual Behavior

    zx is published as an ESM module, so it must be loaded async.

    Steps to Reproduce the Problem

    Create script.ts with

    #!/usr/bin/env ts-node
    import {$} from 'zx';
    import {without} from 'lodash';
    // do stuff
    

    npm install zx @types/lodash lodash npm install -g ts-node typescript ts-node ./script.ts

    Specifications

    • Version: 3.0.0
    • Platform: Linux
    opened by cspotcode 20
  • Feature suggestion: Interactive Shell

    Feature suggestion: Interactive Shell

    I am talking about a proper Bash substitute a marriage of good ideas between Node.js REPL and https://www.npmjs.com/package/vorpal (or "Tiny CLI")

    It will make you famous, a bit more handsome, and you'll be the talk of the town.

    Yes.

    feature 
    opened by catpea 20
  • feat: provide logger customization

    feat: provide logger customization

    closes #376 closes #405

    • [x] Tests pass
    • [x] Appropriate changes to README are included in PR
    • [x] Types updated
    test('logger works', async () => {
      let stdout = ''
      let stderr = ''
      $.logFormat = (msg) => msg.map((m) => m.toUpperCase())
      $.logPrint = (data, err) => {
        if (data) stdout += data
        if (err) stderr += err
      }
      $.logIgnore = ['b*', 'fetch']
      // NOTE first arg declares scope
      log('foo', 'foo-test')
      log('bar', 'bar-test')
      log('baz', 'baz-test')
      log('fetch', 'example.com')
    
      assert(stdout.includes('FOO-TEST'))
      assert(!stdout.includes('BAR-TEST'))
      assert(!stdout.includes('BAZ-TEST'))
      assert(!stdout.includes('EXAMPLE.COM'))
    
      $.logOutput = 'stderr'
      log('foo', 'foo')
      assert(stderr.includes('FOO'))
    
      delete $.logPrint
      delete $.logFormat
      delete $.logIgnore
      delete $.logOutput
    })
    

    ignore dep already comes with globby.

    image

    Pros

    • filtering events by their scope: cd/fetch/cmd,...
    • single entry point to introduce creds concealer
    • custom formatting to attach mdc, timestamps, etc
    • logging rules are attached to current ctx, so they can be applied to a cmd block.
    • applied to spawned processes outputs
    • if (!verbose) return at single place

    Cons

    • inherits verbose flag
    • too imperative, +4 config options
    • ~~not applied to spawned processes outputs, they just go as is to the root process stream~~
    opened by antongolub 19
  • Add npm prompts package for command line prompts support with other features

    Add npm prompts package for command line prompts support with other features

    Fixes #<issue_number_goes_here>

    It's a good idea to open an issue first for discussion.

    • [ ] Tests pass
    • [ ] Appropriate changes to README are included in PR
    • [x] Types updated
    opened by OptimBro 17
  • Stdout gets truncated

    Stdout gets truncated

    Expected Behavior

    Capture the entire output of the program in the result.

    Actual Behavior

    zx <<'EOF'
    await $`unimported`
    EOF
    

    Produces result, but cuts off half-way:

    02:26:50 ➜ zx <<'EOF'
    await $`unimported`
    EOF
    $ unimported
    - initializing
    
           summary               unimported v1.19.1 (node)
    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
           entry file 1        : node_modules/@braintree/sanitize-url/src/__tests__
           entry file 2        : node_modules/@braintree/sanitize-url/src/__tests__/test.ts
           entry file 3        : node_modules/@braintree/sanitize-url/src/__tests__/tsconfig.json
           entry file 4        : node_modules/@graphql-tools/relay-operation-optimizer/node_modules/immutable/contrib/cursor/__tests__
           entry file 5        : node_modules/@graphql-tools/relay-operation-optimizer/node_modules/immutable/contrib/cursor/__tests__/Cursor.ts
           entry file 6        : node_modules/@hookform/resolvers/class-validator/src/__tests__
           entry file 7        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__fixtures__
           entry file 8        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__fixtures__/data.ts
           entry file 9        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__snapshots__
           entry file 10       : node_modules/@hookform/resolvers/class-validator/src/__tests__/__snapshots__/class-validator.ts.snap
           entry file 11       : node_modules/@hookform/resolvers/class-validator/src/__tests__/class-validator.ts
           entry file 12       : node_modules/@hookform/resolvers/class-validator/src/__tests__/Form-native-validation.tsx
           entry file 13       : node_modules/@hookform/resolvers/class-validator/src/__tests__/Form.tsx
           entry file 14       : node_modules/@hookform/resolvers/computed-types/src/__tests__
           entry file 15       : node_modules/@hookform/resolvers/computed-types/src/__tests__/__fixtures__
           entry file 16       : node_modules/@hookform/resolvers/computed-types/src/__tests__/__fixtures__/data.ts
           entry file 17       : node_modules/@hookform/resolvers/computed-types/src/__tests__/__snapshots__
           entry file 18       : node_modules/@hookform/resolvers/computed-types/src/__tests__/__snapshots__/computed-types.ts.snap
           entry file 19       : node_modules/@hookform/resolvers/computed-types/src/__tests__/computed-types.ts
           entry file 20       : node_modules/@hookform/resolvers/computed-types/src/__tests__/Form-native-validation.tsx
           entry file 21       : node_modules/@hookform/resolvers/computed-types/src/__tests__/Form.tsx
           entry file 22       : node_modules/@hookform/resolvers/io-ts/src/__tests__
           entry file 23       : node_modules/@hookform/resolvers/io-ts/src/__tests__/__fixtures__
           entry file 24       : node_modules/@hookform/resolvers/io-ts/src/__tests__/__fixtures__/data.ts
           entry file 25       : node_modules/@hookform/resolvers/io-ts/src/__tests__/__snapshots__
           entry file 26       : node_modules/@hookform/resolvers/io-ts/src/__tests__/__snapshots__/io-ts.ts.snap
           entry file 27       : node_modules/@hookform/resolvers/io-ts/src/__tests__/Form-native-validation.tsx
           entry file 28       : node_modules/@hookform/resolvers/io-ts/src/__tests__/Form.tsx
           entry file 29       : node_modules/@hookform/resolvers/io-ts/src/__tests__/io-ts.ts
           entry file 30       : node_modules/@hookform/resolvers/joi/src/__tests__
           entry file 31       : node_modules/@hookform/resolvers/joi/src/__tests__/__fixtures__
           entry file 32       : node_modules/@hookform/resolvers/joi/src/__tests__/__fixtures__/data.ts
           entry file 33       : node_modules/@hookform/resolvers/joi/src/__tests__/__snapshots__
           entry file 34       : node_modules/@hookform/resolvers/joi/src/__tests__/__snapshots__/joi.ts.snap
           entry file 35       : node_modules/@hookform/resolvers/joi/src/__tests__/Form-native-validation.tsx
           entry file 36       : node_modules/@hookform/resolvers/joi/src/__tests__/Form.tsx
           entry file 37       : node_modules/@hookform/resolvers/joi/src/__tests__/joi.ts
           entry file 38       : node_modules/@hookform/resolvers/nope/src/__tests__
           entry file 39       : node_modules/@hookform/resolvers/nope/src/__tests__/__fixtures__
           entry file 40       : node_modules/@hookform/resolvers/nope/src/__tests__/__fixtures__/data.ts
           entry file 41       : node_modules/@hookform/resolvers/nope/src/__tests__/__snapshots__
           entry file 42       : node_modules/@hookform/resolvers/nope/src/__tests__/__snapshots__/nope.ts.snap
           entry file 43       : node_modules/@hookform/resolvers/nope/src/__tests__/Form-native-validation.tsx
           entry file 44       : node_modules/@hookform/resolvers/nope/src/__tests__/Form.tsx
           entry file 45       : node_modules/@hookform/resolvers/nope/src/__tests__/nope.ts
           entry file 46       : node_modules/@hookform/resolvers/superstruct/src/__tests__
           entry file 47       : node_modules/@hookform/resolvers/superstruct/src/__tests__/__fixtures__
           entry file 48       : node_modules/@hookform/resolvers/superstruct/src/__tests__/__fixtures__/data.ts
           entry file 49       : node_modules/@hookform/resolvers/superstruct/src/__tests__/__snapshots__
           entry file 50       : node_modules/@hookform/resolvers/superstruct/src/__tests__/__snapshots__/superstruct.ts.snap
           entry file 51       : node_modules/@hookform/resolvers/superstruct/src/__tests__/Form-native-validation.tsx
           entry file 52       : node_modules/@hookform/resolvers/superstruct/src/__tests__/Form.tsx
           entry file 53       : node_modules/@hookform/resolvers/superstruct/src/__tests__/superstruct.ts
           entry file 54       : node_modules/@hookform/resolvers/typanion/src/__tests__
           entry file 55       : node_modules/@hookform/resolvers/typanion/src/__tests__/__fixtures__
           entry file 56       : node_modules/@hookform/resolvers/typanion/src/__tests__/__fixtures__/data.ts
           entry file 57       : node_modules/@hookform/resolvers/typanion/src/__tests__/__snapshots__
           entry file 58       : node_modules/@hookform/resolvers/typanion/src/__tests__/__snapshots__/typanion.ts.snap
           entry file 59       : node_modules/@hookform/resolvers/typanion/src/__tests__/Form-native-validation.tsx
           entry file 60       : node_modules/@hookform/resolvers/typanion/src/__tests__/Form.tsx
           entry file 61       : node_modules/@hookform/resolvers/typanion/src/__tests__/typanion.ts
           entry file 62       : node_modules/@hookform/resolvers/vest/src/__tests__
           entry file 63       : node_modules/@hookform/resolvers/vest/src/__tests__/__fixtures__
           entry file 64       : node_modules/@hookform/resolvers/vest/src/__tests__/__fixtures__/data.ts
           entry file 65       : node_modules/@hookform/resolvers/vest/src/__tests__/__snapshots__
           entry file 66       : node_modules/@hookform/resolvers/vest/src/__tests__/__snapshots__/vest.ts.snap
           entry file 67       : node_modules/@hookform/resolvers/vest/src/__tests__/Form-native-validation.tsx
           entry file 68       : node_modules/@hookform/resolvers/vest/src/__tests__/Form.tsx
           entry file 69       : node_modules/@hookform/resolvers/vest/src/__tests__/vest.ts
           entry file 70       : node_modules/@hookform/resolvers/yup/src/__tests__
           entry file 71       : node_modules/@hookform/resolvers/yup/src/__tests__/__fixtures__
           entry file 72       : node_modules/@hookform/resolvers/yup/src/__tests__/__fixtures__/data.ts
           entry file 73       : node_modules/@hookform/resolvers/yup/src/__tests__/__snapshots__
           entry file 74       : node_modules/@hookform/resolvers/yup/src/__tests__/__snapshots__/yup.ts.snap
           entry file 75       : node_modules/@hookform/resolvers/yup/src/__tests__/Form-native-validation.tsx
           entry file 76       : node_modules/@hookform/resolvers/yup/src/__tests__/Form.tsx
           entry file 77       : node_modules/@Error: - initializing
        at file:///private/var/folders/j0/ljpkc6ps1kb6x0ts9fmzmx3h0000gn/T/l82xxonpucp.mjs:1:8
        exit code: 1
    contra-web-app on master [$] took 57s
    

    Expected behavior

    Result of the program is captured in its entirety.

    The actual output is a lot longer and ends with a summary:

    02:27:53 ➜ unimported
    
           summary               unimported v1.19.1 (node)
    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
           entry file 1        : node_modules/@braintree/sanitize-url/src/__tests__
           entry file 2        : node_modules/@braintree/sanitize-url/src/__tests__/test.ts
           entry file 3        : node_modules/@braintree/sanitize-url/src/__tests__/tsconfig.json
           entry file 4        : node_modules/@graphql-tools/relay-operation-optimizer/node_modules/immutable/contrib/cursor/__tests__
           entry file 5        : node_modules/@graphql-tools/relay-operation-optimizer/node_modules/immutable/contrib/cursor/__tests__/Cursor.ts
           entry file 6        : node_modules/@hookform/resolvers/class-validator/src/__tests__
           entry file 7        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__fixtures__
           entry file 8        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__fixtures__/data.ts
           entry file 9        : node_modules/@hookform/resolvers/class-validator/src/__tests__/__snapshots__
           entry file 10       : node_modules/@hookform/resolvers/class-validator/src/__tests__/__snapshots__/class-validator.ts.snap
           entry file 11       : node_modules/@hookform/resolvers/class-validator/src/__tests__/class-validator.ts
           entry file 12       : node_modules/@hookform/resolvers/class-validator/src/__tests__/Form-native-validation.tsx
           entry file 13       : node_modules/@hookform/resolvers/class-validator/src/__tests__/Form.tsx
    [trimmed]
    ─────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
         β”‚ 7 unimported files
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 β”‚ src/components/ContraImage/ContraImage.tsx
       2 β”‚ src/features/profile/components/ProfileSocialLinks/ProfileSocialLinks.tsx
       3 β”‚ src/features/project/components/Project/ProjectExpandedCollaboratorsList/ProjectExpandedCollaboratorsList.tsx
       4 β”‚ src/features/project/components/Project/ProjectMinimizedCollaboratorsList/ProjectMinimizedCollaboratorsList.tsx
       5 β”‚ src/features/project/components/Project/ProjectPartnerWithAction/ProjectPartnerWithAction.tsx
       6 β”‚ src/features/project/components/Project/ProjectRolesToolsOrganizations/ProjectRolesToolsOrganizations.tsx
       7 β”‚ src/features/work/components/Contract/InquirySent/InquirySent.tsx
    ─────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    
    
           Inspect the results and run npx unimported -u to update ignore lists
    

    Specifications

    • https://github.com/smeijer/unimported
    • Version: 4.2.0
    • Platform: node v16.1.0 / macos
    feature 
    opened by gajus 16
  • Feature Request: Expose command escaper

    Feature Request: Expose command escaper

    Expected Behavior

    When running commands inside of commands (subshells) I want a way to recursively escape them. e.g.

    const command = `php artisan -vvv routes:javascript ${filename} --path ${tmpDir} && cat ${fullPath}`
    const output = (await $`kubectl --context docker-desktop exec deploy/kmb-app-deployment-development -cphp -- sh -c ${command}`.quiet()).stdout
    

    command is nicely escaped which makes it easy to pass to sh -c, but ${filename} and ${tmpDir} are not. I would like to write that as

    const command = $.escape`php artisan ...`
    

    And it would give me back a fully escaped command but as a string, without executing it.

    I believe I can do it like this now:

    const fullPath = path.join(tmpDir, filename)
    const command = `php artisan -vvv routes:javascript ${$.quote(filename)} --path ${$.quote(tmpDir)} && cat ${$.quote(fullPath)}`
    

    But it's not as convenient as the auto-escaping that $ does.

    Actual Behavior

    Have to manually escape sub-commands.

    Steps to Reproduce the Problem

    Examples above.

    Specifications

    • Version: 7.1.1
    • Platform: Ubuntu 20.04.2 LTS
    opened by mnpenner 2
  • Feature Request: Less quiet (print command but not stdout)

    Feature Request: Less quiet (print command but not stdout)

    Expected Behavior

    const output = (await $`hg root`.quiet({cmd:true)).stdout
    // or
    const output = await $`hg root`.stdoutPromise()
    // or
    const output = await $.stdout`...`
    

    Will print

    $ hg root
    

    But not stdout.

    That way I can see what commands are executing without cluttering my terminal with long outputs, particularly ones that I'm going to use in subsequent commands anyway.

    Actually it would be nice to have .stdoutPromise() or whatever it should be called trim any trailing whitespace too.

    Also, I think it can still print stderr to the terminal. It's just stdout I want to suppress when I'm capturing it anyway.

    Actual Behavior

    const projectDir = (await $`hg root`).stdout
    console.log(projectDir)
    

    Will print my project dir twice,

    const projectDir = (await $`hg root`.quiet()).stdout
    console.log(projectDir)
    

    Will print my project dir once, like I want, but also doesn't show me the command it executed.

    Steps to Reproduce the Problem

    See above.

    Specifications

    • Version: 7.1.1
    • Platform: Ubuntu 20.04.2 LTS (under WSL)
    opened by mnpenner 0
  • Feature request: Configuration of minimist

    Feature request: Configuration of minimist

    It is currently difficult to use the minimist integration if you have a combination of boolean arguments and unnamed arguments.

    This example script will fail if the -h option is placed before the filename parameter

    #!/usr/bin/env zx
    
    $.verbose = false
    
    const header = argv.header || argv.h
    const filename = argv._[0]
    
    if (header) {
      console.log('Filename: ', filename)
    }
    
    $`cat ${filename}`.pipe(process.stdout)
    
    // This will fail, since 'myfile.txt' will be interpreted as the value of -h
    myscript.mjs -h myfile.txt
    
    // This will work as expected
    myscript.mjs myfile.txt -h 
    

    A solution to this would be either:

    A) A possibility to provide a configuration for minimist.

    B) Expose the minimist package to the script, so that the script writer may call it directly and provide a configuration

    I believe A) would be better, while B) would be easier to implement

    opened by sstendal 4
  • Feature request: redact secrets when logging

    Feature request: redact secrets when logging

    Current Behavior

    If we make a call with a secret, it will be printed to stdout. note: I'm only including shell examples for brevity, but this applies to most zx logging πŸ™‚ If we wanted to go one step further, there could also be a $.redact(text: string) method

    Example:

    const githubUrl = 'https://api.github.com/organizations';
    const githubToken = 'ghs_xxxxxxxxx';
    await $`curl --silent ${githubUrl} -H "Authorization: Bearer ${githubToken}"`;
    
    // prints:
    // $ curl --silent $'https://api.github.com/organizations' -H "Authorization: Bearer ghs_xxxxxxxxx"
    //                                                                                   ^ exposed!
    

    Requested Behavior

    It would be cool if we could either redact or replace secrets.

    For example, with blanket redaction:

    const githubUrl = 'https://api.github.com/organizations';
    const githubToken = 'ghs_xxxxxxxxx';
    $.secrets.push(githubToken); // Example: Blanket redaction
    await $`curl --silent ${githubUrl} -H "Authorization: Bearer ${githubToken}"`;
    
    // prints:
    // $ curl --silent $'https://api.github.com/organizations' -H "Authorization: Bearer **redacted**"
    //                                                                                   ^ hidden
    

    Or with contextual redaction:

    const githubUrl = 'https://api.github.com/organizations';
    const githubToken = 'ghs_xxxxxxxxx';
    $.secrets.githubToken = githubToken; // Example: Contextual redaction
    await $`curl --silent ${githubUrl} -H "Authorization: Bearer ${githubToken}"`;
    
    // prints:
    // $ curl --silent $'https://api.github.com/organizations' -H "Authorization: Bearer **githubToken**"
    //                                                                                   ^ hidden with context
    

    Steps to Reproduce the Problem

    covered in Current Behavior section

    Specifications

    • Version: 7.1.1
    • Platform: Linux
    opened by vikaspotluri123 5
  • Feature request: wrap enquirer for questions

    Feature request: wrap enquirer for questions

    Currently the question() function is awkward to use when you need a user prompt other than basic text input. Will it be a lot of work to wrap enquirer instead? This will bring a lot of elegance and power to the UI, allowing for example, select and multi-select prompts.

    opened by JannieT 2
Releases(7.1.1)
  • 7.1.1(Oct 7, 2022)

  • 7.1.0(Oct 5, 2022)

    Autoinstall

    This release introduces a new flag --install which will parse and install all required or imported packages automatically.

    import sh from 'tinysh'
    sh.say('Hello, world!')
    

    And running zx --install script.mjs will trigger installation of tinysh package! It even possible to specify a needed version in comment with @ symbol:

    import sh from 'tinysh' // @^1.0.0
    

    PowerShell

    Another big change is for Windows. Now, the default shell on Windows is powershell.exe.

    This should fix a lot of issues with "dollar" signs.

    Fixes

    • Minimist now correctly parses argv when using zx cli.
    Source code(tar.gz)
    Source code(zip)
  • 7.0.8(Aug 1, 2022)

    What's Changed

    • fix: avoid redundant $.verbose reassignment on CLI init by @antongolub in https://github.com/google/zx/pull/475
    • fix: let $.spawn be configurable by @antongolub in https://github.com/google/zx/pull/486

    Full Changelog: https://github.com/google/zx/compare/7.0.7...7.0.8

    Source code(tar.gz)
    Source code(zip)
  • 7.0.7(Jul 2, 2022)

    • Fixed $ to be more like a normal function in https://github.com/google/zx/pull/472

    Now it's possible to do:

    const my$ = $.bind(null)
    const foo = await my$`echo foo`
    
    Source code(tar.gz)
    Source code(zip)
  • 7.0.6(Jul 1, 2022)

  • 7.0.5(Jun 30, 2022)

  • 7.0.4(Jun 29, 2022)

  • 7.0.3(Jun 27, 2022)

  • 7.0.2(Jun 22, 2022)

  • 7.0.1(Jun 20, 2022)

  • 7.0.0(Jun 14, 2022)

    🐚 zx v7.0.0 release! πŸŽ‰

    A tool for writing better scripts

    zx

    In this release:

    • Fully rewritten in TypeScript.
    • Code coverage is now 99%.
    • New within() API for async contexts.
    • echo() now comes out of the box.
    • Support for --eval flag.
    • REPL support.
    Source code(tar.gz)
    Source code(zip)
  • 6.2.5(Jun 13, 2022)

    What's Changed

    • Fixed question() types by @olebergen in https://github.com/google/zx/pull/437
    • feat: transform some core types into ifaces to let them be extendable by plugins by @antongolub in https://github.com/google/zx/pull/439

    New Contributors

    • @olebergen made their first contribution in https://github.com/google/zx/pull/437

    Full Changelog: https://github.com/google/zx/compare/6.2.4...6.2.5

    Source code(tar.gz)
    Source code(zip)
  • 6.2.4(Jun 8, 2022)

    What's Changed

    • fix: rm redundant root ctx assignment by @antongolub in https://github.com/google/zx/pull/432

    Full Changelog: https://github.com/google/zx/compare/6.2.3...6.2.4

    Source code(tar.gz)
    Source code(zip)
  • 6.2.3(Jun 5, 2022)

    What's Changed

    • Fix promise ctx by @antongolub in https://github.com/google/zx/pull/427

    Full Changelog: https://github.com/google/zx/compare/6.2.2...6.2.3

    Source code(tar.gz)
    Source code(zip)
  • 6.2.2(Jun 4, 2022)

    What's Changed

    • fix getCtx() by @antongolub in https://github.com/google/zx/pull/425

    Full Changelog: https://github.com/google/zx/compare/6.2.1...6.2.2

    Source code(tar.gz)
    Source code(zip)
  • 6.2.1(Jun 3, 2022)

  • 6.2.0(Jun 1, 2022)

  • 6.1.0(Apr 15, 2022)

  • 6.0.7(Mar 21, 2022)

    What's Changed

    • Code coverage increased to 98% by @antongolub in https://github.com/google/zx/pull/359
    • Fixed: do not rely on globals inside experimental.mjs by @antongolub in https://github.com/google/zx/pull/363

    Full Changelog: https://github.com/google/zx/compare/6.0.6...6.0.7

    Source code(tar.gz)
    Source code(zip)
  • 6.0.4(Mar 18, 2022)

    What's Changed

    • Added export for which package as global by @antongolub in https://github.com/google/zx/pull/347
    • Introduced experimental withTimeout helper by @antongolub in https://github.com/google/zx/pull/355

    Full Changelog: https://github.com/google/zx/compare/6.0.3...6.0.4

    Source code(tar.gz)
    Source code(zip)
  • 6.0.2(Mar 17, 2022)

    What's Changed

    • Added new CLI --experimental flag by @antongolub in https://github.com/google/zx/pull/346
    • Fixed bind cwd to spawn call by @antongolub in https://github.com/google/zx/pull/352

    Full Changelog: https://github.com/google/zx/compare/6.0.1...6.0.2

    Source code(tar.gz)
    Source code(zip)
  • 5.3.0(Mar 14, 2022)

    What's Changed

    • Added missing type declaration for quiet func by @svennergr in https://github.com/google/zx/pull/332
    • Added optional delay for retry by @antongolub in https://github.com/google/zx/pull/335
    • New config spawn func by @antongolub in https://github.com/google/zx/pull/333
    • New config maxBuffer option by @antongolub in https://github.com/google/zx/pull/337
    • Updated node-fetch to v3.2.3 by @antongolub in https://github.com/google/zx/pull/336
    Source code(tar.gz)
    Source code(zip)
  • 5.2.0(Feb 27, 2022)

    72c8cf0 Added experimental startSpinner() function. 53a215e Added the signal field to ProcessOutput. bf88f50 Added quiet() function (#313). 51fb6d5 Improved experemental echo()function.

    import {echo} from 'zx/experimental'
    
    // The echo() can be used both ways: 
    echo`Current branch is ${branch}.`
    // or
    echo('Current branch is', branch)
    
    import {startSpinner} from 'zx/experimental'
    
    let stop = startSpinner()
    await $`long-running command`
    stop()
    
    let p = nothrow($`while true; do :; done`)
    setTimeout(() => p.kill('SIGKILL'), 1000)
    let {signal} = await p
    assert.equal(signal, 'SIGKILL')
    
    await quiet($`grep something from-file`)
    // Command and output will not be displayed.
    
    Source code(tar.gz)
    Source code(zip)
  • 5.1.0(Feb 16, 2022)

    98a9abb Added new experimental retry & echo functions. f8bb1c7 Fixed bug where running script without extension removes existing .mjs files (#276). 792370a Increased maxBuffer to 200 MiB. 7fafa26 Added known issues doc.

    Example:

    import {echo, retry} from 'zx/experimental'
    
    let branch = await $`git branch --show-current`
    echo`Current branch is ${branch}.`
    
    let {stdout} = await retry(5)`curl localhost`
    
    Source code(tar.gz)
    Source code(zip)
  • 5.0.0(Feb 9, 2022)

       __/\\\\\\\\\\\__/\\\____/\\\_     
        _\///////\\\/__\///\\\/\\\/__    
         ______/\\\/______\///\\\/____   
          ____/\\\/_________/\\\/\\\___  
           __/\\\\\\\\\\\__/\\\/\///\\\_ 
            _\///////////__\///____\///__
    

    Updated dependencies

     chalk               ^4.1.2  β†’   ^5.0.0
     globby             ^12.0.1  β†’  ^13.1.1
     node-fetch          ^2.6.1  β†’   ^3.2.0
    

    Added feature

    Added YAML package.

    let {foo} = YAML.parse('foo: bar')
    

    Breaking changes

    This release drops build of CommonJS version and support for .ts extension by zx bin.

    TypeScript is still supported, for example, via ts-node:

    node --loader ts-node/esm script.ts
    

    Also, a new Node version requirement is >= 16.0.0.

    Source code(tar.gz)
    Source code(zip)
  • 4.3.0(Jan 16, 2022)

    • 3f7c8ca Fixed --version argv parsing.
    • fe083c4 Fixed a process kill() with unhandled exception.
    • 5f54045 Fixed cd() to work with relative paths.
    let p = $`sleep 99999`
    await sleep(100)
    p.kill()
    
    Source code(tar.gz)
    Source code(zip)
  • 4.2.0(Sep 7, 2021)

  • 4.1.1(Aug 31, 2021)

  • 4.1.0(Aug 31, 2021)

  • 4.0.0(Aug 27, 2021)

    In this release:

    • Added kill() method 4915ff3
    • Added path package ce69a84
    • Added cjs support (#203)
    • Changed: register globals only in zx binary 33aaae4

    New examples:

    let {stdin, stdout} = $`npm init`
    
    for await (let chunk of stdout) {
      if (chunk.includes('package name:')) stdin.write('test\n')
    }
    
    Source code(tar.gz)
    Source code(zip)
Owner
Google
Google ❀️ Open Source
Google
✍️ Generative creative writing via OpenAI

The stars have always been a source of wonder and mystery. They have inspired poets and philosophers throughout the ages. Arthur Conan Doyle was no ex

Nate Goldman 0 Dec 24, 2022
JSON Diff Kit - A better JSON differ & viewer

A better JSON differ & viewer, support LCS diff for arrays and recognise some changes as modification apart from simple remove+add.

Rex Zeng 38 Dec 18, 2022
Grupprojekt fΓΆr kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet fΓΆr kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide FΓΆr information om hur utv

Svante Jonsson IT-HΓΆgskolan 3 May 18, 2022
Hemsida fΓΆr personer i Sverige som kan och vill erbjuda boende till mΓ€nniskor pΓ₯ flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo fΓΆr kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
⚑️ Makes writing scripts absurdly easy

haxx easily create and run scripts using javascript getting started β€’ commands β€’ api Features ?? Process DX - easily manage and run processes ?? Javas

henrycunh 9 Apr 15, 2022
➀_ A better way to organize your npm scripts

➀_ Better Scripts A better way to organize your npm scripts See better-scripts.vercel.app β†’ Installation Usage Basic setup Add script description Scri

Yoki 49 Dec 29, 2022
dexy 2.2 1.9 L4 Python is a free-form literate documentation tool for writing any kind of technical document incorporating code.

Dexy Dexy is open source automation software with features especially designed for documentation and reporting. More information at http://dexy.it Doc

null 304 Sep 30, 2022
WriterAI is an AI based content writing tool that helps users easily write high quality emails, blogs, letters, thesis and other stuff.

WriterAI is an AI based content writing tool that helps users easily write high quality emails, blogs, letters, thesis and other stuff. One can also share their project with others and work as a team.

Ishant Chauhan 67 Jan 2, 2023
A tool to document your package.json scripts

why A tool to document your package.json scripts Why why? As your project grows you add more scripts to package.json. When a new member joins the proj

Dragoș Străinu 13 Sep 22, 2022
A tool to document your package.json scripts

why A tool to document your package.json scripts Why why? As your project grows you add more scripts to package.json. When a new member joins the proj

Planable Inc. 13 Sep 22, 2022
A command-line tool to manage Deno scripts installed via deno install

??️ nublar nublar is a command-line tool to manage your scripts installed via deno install. ??️ Installation deno install --allow-read --allow-write -

Shun Ueda 16 Dec 26, 2022
Digispark Overmaster : free IDE TOOL allows to create and edit Digispark Scripts by the drag and drop technique,with cool GUI and easy to use it

Digispark_Overmaster Digispark Overmaster : free IDE TOOL allows to create and edit Digispark Scripts by the drag and drop technique,with cool GUI and

Yehia Elborma 5 Nov 14, 2022
Components for interactive scientific writing, reactive documents and explorable explanations.

@curvenote/article The goal of @curvenote/article is to provide web-components for interactive scientific writing, reactive documents and explorable e

curvenote 142 Dec 24, 2022
🐠 Babel is a compiler for writing next generation JavaScript.

The compiler for writing next generation JavaScript. Supporting Babel Babel (pronounced "babble") is a community-driven project used by many companies

Babel 41.8k Jan 9, 2023
Validate your forms, frontend, without writing a single line of javascript

Parsley JavaScript form validation, without actually writing a single line of JavaScript! Version 2.9.2 Doc See index.html and doc/ Requirements jQuer

Guillaume Potier 9k Dec 27, 2022
A rich text editor for everyday writing

Trix A Rich Text Editor for Everyday Writing Compose beautifully formatted text in your web application. Trix is a WYSIWYG editor for writing messages

Basecamp 17.3k Jan 3, 2023
Principles of writing consistent, idiomatic CSS.

Principles of writing consistent, idiomatic CSS The following document outlines a reasonable style guide for CSS development. These guidelines strongl

Nicolas Gallagher 6.5k Dec 30, 2022
πŸ”¬ Writing reliable & fault-tolerant microservices in Node.js

A Node.js microservices toolkit for the NATS messaging system Run on repl.it Node: v6+ Documentation: https://hemerajs.github.io/hemera/ Lead Maintain

HemeraJs 800 Dec 14, 2022
🐠 Babel is a compiler for writing next generation JavaScript.

The compiler for writing next generation JavaScript. Supporting Babel Babel (pronounced "babble") is a community-driven project used by many companies

Babel 41.8k Jan 8, 2023