Commit b659bfb

Anton Medvedev <anton@medv.io>
2022-06-02 23:20:42
Add --eval option
1 parent 077d4c4
Changed files (3)
src/cli.ts
@@ -21,7 +21,7 @@ import { basename, dirname, extname, join, resolve } from 'node:path'
 import url from 'node:url'
 import { $, argv, fetch, ProcessOutput, chalk } from './index.js'
 import { startRepl } from './repl.js'
-import { randomId } from './util.js'
+import { randomId, stdin } from './util.js'
 import './globals.js'
 
 await (async function main() {
@@ -49,6 +49,16 @@ await (async function main() {
       return (process.exitCode = 0)
     }
   }
+  if (argv.eval || argv.e) {
+    Object.defineProperty(global, 'stdin', {
+      configurable: false,
+      enumerable: true,
+      get: stdin,
+    })
+    let script = (argv.eval || argv.e).toString()
+    await runScript(addLogOnLastLine(script))
+    return (process.exitCode = 0)
+  }
   let firstArg = process.argv.slice(2).find((a) => !a.startsWith('--'))
   if (typeof firstArg === 'undefined' || firstArg === '-') {
     let ok = await scriptFromStdin()
@@ -81,6 +91,17 @@ await (async function main() {
   process.exitCode = 1
 })
 
+function addLogOnLastLine(script: string) {
+  let lines = script.trim().split('\n')
+  return lines.slice(0, -1).join('\n') + '\n' + `console.log(${lines.at(-1)})`
+}
+
+async function runScript(script: string) {
+  let filepath = join(tmpdir(), randomId() + '.mjs')
+  await fs.mkdtemp(filepath)
+  await writeAndImport(script, filepath, join(process.cwd(), 'stdin.mjs'))
+}
+
 async function scriptFromStdin() {
   let script = ''
   if (!process.stdin.isTTY) {
@@ -90,9 +111,7 @@ async function scriptFromStdin() {
     }
 
     if (script.length > 0) {
-      let filepath = join(tmpdir(), randomId() + '.mjs')
-      await fs.mkdtemp(filepath)
-      await writeAndImport(script, filepath, join(process.cwd(), 'stdin.mjs'))
+      await runScript(script)
       return true
     }
   }
@@ -236,6 +255,7 @@ function printUsage() {
    --shell=<path>      custom shell binary
    --prefix=<command>  prefix all commands
    --interactive, -i   start repl
+   --eval=<js>, -e     evaluate script 
    --experimental      enable new api proposals
    --version, -v       print current zx version
    --help, -h          print help
src/util.ts
@@ -97,3 +97,19 @@ export function exitCodeInfo(exitCode: number | null): string | undefined {
     159: 'Bad syscall',
   }[exitCode || -1]
 }
+
+export function stdin() {
+  try {
+    if (process.env.FX_ASYNC_STDIN == 'true') throw 'yes'
+    return fs.readFileSync(process.stdin.fd).toString()
+  } catch (err) {
+    return (async function () {
+      let buf = ''
+      process.stdin.setEncoding('utf8')
+      for await (const chunk of process.stdin) {
+        buf += chunk
+      }
+      return buf
+    })()
+  }
+}
test/cli.test.js
@@ -118,4 +118,20 @@ test('exceptions are caught', async () => {
   assert.match(out2.stderr, '42')
 })
 
+test('eval works', async () => {
+  let { stdout } = await $`node build/cli.js --eval '69'`
+  assert.is(stdout, '69\n')
+})
+
+test('eval works with stdin', async () => {
+  let { stdout } =
+    await $`printf "Hello world" | node build/cli.js --eval='stdin'`
+  assert.is(stdout, 'Hello world\n')
+})
+
+test('--eval works with async stdin', async () => {
+  let p = $`(printf foo; sleep 0.1; printf bar) | FX_ASYNC_STDIN=true node build/cli.js --eval 'await stdin'`
+  assert.is((await p).stdout, 'foobar\n')
+})
+
 test.run()