Commit 934b64a

Anton Medvedev <anton@medv.io>
2021-05-23 14:26:57
Add .ts files support
1 parent 78c04fa
examples/typescript.ts
@@ -0,0 +1,22 @@
+#!/usr/bin/env zx
+
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// <reference types=".." />
+
+void async function () {
+  await $`pwd`
+}()
+
index.d.ts
@@ -14,9 +14,13 @@
 
 import {ChildProcess} from 'child_process'
 import {Readable, Writable} from 'stream'
+import {createReadStream, createWriteStream, promises as _fs} from 'fs'
+import * as _os from 'os'
+import * as _chalk from 'chalk'
 
 interface $ {
   (pieces: TemplateStringsArray, ...args: any[]): ProcessPromise<ProcessOutput>
+
   verbose: boolean
   shell: string
   cwd: string
@@ -24,26 +28,37 @@ interface $ {
   quote: (input: string) => string
 }
 
-export const $: $
-
-export function cd(path: string): void
-
-export function question(query?: string, options?: QuestionOptions): Promise<string>
+export type cd = (path: string) => void
+export type sleep = (ms: number) => Promise<void>
+export type question = (query?: string, options?: QuestionOptions) => Promise<string>
 export type QuestionOptions = { choices: string[] }
 
-export function sleep(ms: number): Promise<void>
-
 export interface ProcessPromise<T> extends Promise<T> {
   child: ChildProcess
   readonly stdin: Writable
   readonly stdout: Readable
   readonly stderr: Readable
-  pipe(dest: ProcessPromise<ProcessOutput>|Writable): ProcessPromise<ProcessOutput>
+
+  pipe(dest: ProcessPromise<ProcessOutput> | Writable): ProcessPromise<ProcessOutput>
 }
 
 export class ProcessOutput {
   readonly exitCode: number
   readonly stdout: string
   readonly stderr: string
+
   toString(): string
 }
+
+declare global {
+  export const $: $
+  export const cd: cd
+  export const sleep: sleep
+  export const question: question
+  export const chalk: typeof _chalk
+  export const fs: typeof _fs & {
+    createWriteStream: typeof createWriteStream
+    createReadStream: typeof createReadStream
+  }
+  export const os: typeof _os
+}
index.mjs
@@ -12,7 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {existsSync} from 'fs'
+import {
+  createReadStream,
+  createWriteStream,
+  existsSync,
+  promises as fs
+} from 'fs'
+import os from 'os'
 import {promisify} from 'util'
 import {spawn} from 'child_process'
 import {createInterface} from 'readline'
@@ -21,21 +27,6 @@ import which from 'which'
 import chalk from 'chalk'
 import shq from 'shq'
 
-export {chalk}
-
-function colorize(cmd) {
-  return cmd.replace(/^\w+(\s|$)/, substr => {
-    return chalk.greenBright(substr)
-  })
-}
-
-function substitute(arg) {
-  if (arg instanceof ProcessOutput) {
-    return arg.stdout.replace(/\n$/, '')
-  }
-  return arg.toString()
-}
-
 export function $(pieces, ...args) {
   let __from = (new Error().stack.split('at ')[2]).trim()
   let cmd = pieces[0], i = 0
@@ -206,3 +197,29 @@ export class ProcessOutput extends Error {
     return this.#code
   }
 }
+
+function colorize(cmd) {
+  return cmd.replace(/^\w+(\s|$)/, substr => {
+    return chalk.greenBright(substr)
+  })
+}
+
+function substitute(arg) {
+  if (arg instanceof ProcessOutput) {
+    return arg.stdout.replace(/\n$/, '')
+  }
+  return arg.toString()
+}
+
+Object.assign(global, {
+  $,
+  cd,
+  chalk,
+  fetch,
+  fs: {...fs, createWriteStream, createReadStream},
+  os,
+  question,
+  sleep,
+})
+
+export {chalk}
zx.mjs
@@ -14,23 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {join, basename, extname, resolve, dirname} from 'path'
-import os, {tmpdir} from 'os'
-import {promises as fs, createWriteStream, createReadStream} from 'fs'
+import {basename, dirname, extname, join, parse, resolve} from 'path'
+import {tmpdir} from 'os'
+import {promises as fs} from 'fs'
 import {createRequire} from 'module'
 import url from 'url'
-import {$, cd, question, fetch, chalk, sleep, ProcessOutput} from './index.mjs'
-
-Object.assign(global, {
-  $,
-  cd,
-  fetch,
-  question,
-  chalk,
-  sleep,
-  fs: {...fs, createWriteStream, createReadStream},
-  os
-})
+import {$, fetch, ProcessOutput} from './index.mjs'
 
 try {
   let firstArg = process.argv[2]
@@ -125,6 +114,15 @@ async function importPath(filepath, origin = filepath) {
       origin,
     )
   }
+  if (ext === '.ts') {
+    let {dir, name} = parse(filepath)
+    let outFile = join(dir, name + '.mjs')
+    await $`tsc --target esnext --module esnext --moduleResolution node ${filepath}`
+    await fs.rename(join(dir, name + '.js'), outFile)
+    let wait = importPath(outFile, filepath)
+    await fs.rm(outFile)
+    return wait
+  }
   let __filename = resolve(origin)
   let __dirname = dirname(__filename)
   let require = createRequire(origin)