Commit 73e3e1d

Anton Golub <antongolub@antongolub.com>
2024-05-13 18:48:42
feat: add `tempdir` and `tempfile` utils (#803)
* test: add ts smoke tests * test: fix ts smoke test * fix(types): add @webpod/ingrid to dts bundle * ci: print TS version * feat: introduce `tempdir` and `tempfile` utils
1 parent 7384185
src/cli.ts
@@ -59,8 +59,7 @@ const argv = minimist(process.argv.slice(2), {
 })
 
 ;(async function main() {
-  const globals = './globals.js'
-  await import(globals)
+  await import('./globals.js')
   if (argv.verbose) $.verbose = true
   if (argv.quiet) $.verbose = false
   if (argv.shell) $.shell = argv.shell
src/globals.ts
@@ -41,12 +41,16 @@ declare global {
   var quote: typeof _.quote
   var quotePowerShell: typeof _.quotePowerShell
   var retry: typeof _.retry
-  var usePowerShell: typeof _.usePowerShell
-  var usePwsh: typeof _.usePwsh
-  var useBash: typeof _.useBash
   var sleep: typeof _.sleep
   var spinner: typeof _.spinner
   var stdin: typeof _.stdin
+  var tempdir: typeof _.tempdir
+  var tempfile: typeof _.tempfile
+  var tmpdir: typeof _.tempdir
+  var tmpfile: typeof _.tempfile
+  var usePowerShell: typeof _.usePowerShell
+  var usePwsh: typeof _.usePwsh
+  var useBash: typeof _.useBash
   var which: typeof _.which
   var within: typeof _.within
   var YAML: typeof _.YAML
src/index.ts
@@ -27,7 +27,15 @@ export {
   glob as globby,
 } from './vendor.js'
 
-export { type Duration, quote, quotePowerShell } from './util.js'
+export {
+  type Duration,
+  quote,
+  quotePowerShell,
+  tempdir,
+  tempdir as tmpdir,
+  tempfile,
+  tempfile as tmpfile,
+} from './util.js'
 
 /**
  *  @deprecated Use $`cmd`.nothrow() instead.
src/util.ts
@@ -12,7 +12,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { chalk, parseLine } from './vendor.js'
+import os from 'node:os'
+import path from 'node:path'
+import { chalk, parseLine, fs } from './vendor.js'
+
+export function tempdir(prefix = `zx-${randomId()}`) {
+  const dirpath = path.join(os.tmpdir(), prefix)
+  fs.mkdirSync(dirpath, { recursive: true })
+
+  return dirpath
+}
+
+export function tempfile(name?: string, data?: string | Buffer) {
+  const filepath = name
+    ? path.join(tempdir(), name)
+    : path.join(os.tmpdir(), `zx-${randomId()}`)
+
+  if (data === undefined) fs.closeSync(fs.openSync(filepath, 'w'))
+  else fs.writeFileSync(filepath, data)
+
+  return filepath
+}
 
 export function noop() {}
 
test/index.test.js
@@ -51,6 +51,10 @@ import {
   expBackoff,
   spinner,
   path,
+  tempdir,
+  tempfile,
+  tmpdir,
+  tmpfile,
 } from '../build/index.js'
 
 describe('index', () => {
@@ -100,5 +104,9 @@ describe('index', () => {
     // utils
     assert(quote)
     assert(quotePowerShell)
+    assert(tempdir)
+    assert(tmpdir)
+    assert(tmpfile)
+    assert(tempfile)
   })
 })
test/util.test.js
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import assert from 'node:assert'
+import fs from 'node:fs'
 import { test, describe } from 'node:test'
 import {
   exitCodeInfo,
@@ -26,6 +27,8 @@ import {
   randomId,
   normalizeMultilinePieces,
   getCallerLocationFromString,
+  tempdir,
+  tempfile,
 } from '../build/util.js'
 
 describe('util', () => {
@@ -148,3 +151,17 @@ test(`getCallerLocationFromString-JSC`, () => {
   `
   assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/)
 })
+
+test('tempdir() creates temporary folders', () => {
+  assert.match(tempdir(), /\/zx-/)
+  assert.match(tempdir('foo'), /\/foo$/)
+})
+
+test('tempfile() creates temporary files', () => {
+  assert.match(tempfile(), /\/zx-.+/)
+  assert.match(tempfile('foo.txt'), /\/zx-.+\/foo\.txt$/)
+
+  const tf = tempfile('bar.txt', 'bar')
+  assert.match(tf, /\/zx-.+\/bar\.txt$/)
+  assert.equal(fs.readFileSync(tf, 'utf-8'), 'bar')
+})