Commit 958611c

Anton Medvedev <anton@medv.io>
2022-06-08 00:55:54
Split process.cwd() and $.cwd
1 parent b4d1fb8
src/core.ts
@@ -35,9 +35,12 @@ export type Shell = (
   ...args: any[]
 ) => ProcessPromise
 
+const processCwd = Symbol('processCwd')
+
 export type Options = {
+  [processCwd]: string
+  cwd?: string
   verbose: boolean
-  cwd: string
   env: NodeJS.ProcessEnv
   shell: string | boolean
   prefix: string
@@ -56,16 +59,10 @@ const hook = createHook({
 })
 hook.enable()
 
-function syncCwd() {
-  if ($.cwd != process.cwd()) {
-    process.chdir($.cwd)
-  }
-}
-
 function initStore(): Options {
   const context = {
+    [processCwd]: process.cwd(),
     verbose: (global as any).ZX_VERBOSE ?? true,
-    cwd: process.cwd(),
     env: process.env,
     shell: true,
     prefix: '',
@@ -135,7 +132,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
   private _timeout?: number
   private _timeoutSignal?: string
   private _resolved = false
-  _piped = false
+  private _piped = false
   _prerun = noop
   _postrun = noop
 
@@ -163,7 +160,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
       verbose: $.verbose && !this._quiet,
     })
     this.child = spawn($.prefix + this._command, {
-      cwd: $.cwd,
+      cwd: $.cwd ?? $[processCwd],
       shell: typeof $.shell === 'string' ? $.shell : true,
       stdio: this._stdio,
       windowsHide: true,
@@ -374,8 +371,16 @@ export class ProcessOutput extends Error {
 
 export function within<R>(callback: () => R): R {
   const result = storage.run({ ...getStore() }, callback)
-  if ($.cwd != process.cwd()) {
-    process.chdir($.cwd)
-  }
+  syncCwd()
   return result
 }
+
+function syncCwd() {
+  if ($[processCwd] != process.cwd()) process.chdir($[processCwd])
+}
+
+export function cd(dir: string) {
+  $.log({ kind: 'cd', dir })
+  process.chdir(dir)
+  $[processCwd] = process.cwd()
+}
src/goods.ts
@@ -44,12 +44,6 @@ export async function fetch(url: RequestInfo, init?: RequestInit) {
   return nodeFetch(url, init)
 }
 
-export function cd(dir: string) {
-  $.log({ kind: 'cd', dir })
-  $.cwd = path.resolve($.cwd, dir)
-  process.chdir($.cwd)
-}
-
 // A console.log() alternative which can take ProcessOutput.
 export function echo(pieces: TemplateStringsArray, ...args: any[]) {
   let msg
src/index.ts
@@ -21,11 +21,11 @@ export {
   ProcessPromise,
   ProcessOutput,
   within,
+  cd,
 } from './core.js'
 
 export {
   argv,
-  cd,
   chalk,
   echo,
   fetch,
test/index.test.js
@@ -282,62 +282,59 @@ test('executes a script from $PATH', async () => {
 
 test('cd() works with relative paths', async () => {
   let cwd = process.cwd()
-  assert.equal($.cwd, cwd)
   try {
     fs.mkdirpSync('/tmp/zx-cd-test/one/two')
     cd('/tmp/zx-cd-test/one/two')
     let p1 = $`pwd`
-    assert.match($.cwd, '/two')
+    assert.is($.cwd, undefined)
     assert.match(process.cwd(), '/two')
 
     cd('..')
     let p2 = $`pwd`
-    assert.match($.cwd, '/one')
+    assert.is($.cwd, undefined)
     assert.match(process.cwd(), '/one')
 
     cd('..')
     let p3 = $`pwd`
-    assert.match($.cwd, '/tmp/zx-cd-test')
+    assert.is($.cwd, undefined)
     assert.match(process.cwd(), '/tmp/zx-cd-test')
 
-    let results = (await Promise.all([p1, p2, p3])).map((p) =>
+    const results = (await Promise.all([p1, p2, p3])).map((p) =>
       path.basename(p.stdout.trim())
     )
-
     assert.equal(results, ['two', 'one', 'zx-cd-test'])
   } catch (e) {
     assert.ok(!e, e)
   } finally {
     fs.rmSync('/tmp/zx-cd-test', { recursive: true })
     cd(cwd)
-    assert.equal($.cwd, cwd)
   }
 })
 
 test('cd() does affect parallel contexts', async () => {
-  let cwd = process.cwd()
-  let resolve, reject
-  let promise = new Promise((...args) => ([resolve, reject] = args))
-
+  const cwd = process.cwd()
   try {
-    fs.mkdirpSync('/tmp/zx-cd-parallel')
-    within(async () => {
-      assert.equal($.cwd, cwd)
-      await sleep(10)
-      cd('/tmp/zx-cd-parallel')
-      assert.match($.cwd, '/zx-cd-parallel')
-      assert.match(process.cwd(), '/zx-cd-parallel')
-    })
-
-    within(async () => {
-      assert.equal($.cwd, cwd)
-      await sleep(20)
-      assert.not.match($.cwd, '/zx-cd-parallel')
-      assert.not.match(process.cwd(), '/zx-cd-parallel')
-      resolve()
-    })
-
-    await promise
+    fs.mkdirpSync('/tmp/zx-cd-parallel/one/two')
+    await Promise.all([
+      within(async () => {
+        assert.is(process.cwd(), cwd)
+        await sleep(1)
+        cd('/tmp/zx-cd-parallel/one')
+        assert.match(process.cwd(), '/tmp/zx-cd-parallel/one')
+      }),
+      within(async () => {
+        assert.is(process.cwd(), cwd)
+        await sleep(2)
+        assert.is(process.cwd(), cwd)
+      }),
+      within(async () => {
+        assert.is(process.cwd(), cwd)
+        await sleep(3)
+        $.cwd = '/tmp/zx-cd-parallel/one/two'
+        assert.is(process.cwd(), cwd)
+        assert.match((await $`pwd`).stdout, '/tmp/zx-cd-parallel/one/two')
+      }),
+    ])
   } catch (e) {
     assert.ok(!e, e)
   } finally {