Commit be335f1

Anton Golub <antongolub@antongolub.com>
2025-03-16 16:58:05
ci: configure zx-lite publishing (#1131)
1 parent 8cb4212
.github/workflows/dev-publish.yml
@@ -20,7 +20,7 @@ jobs:
       - run: npm test
         env:
           FORCE_COLOR: 3
-      - run: node scripts/clean-package-json.mjs
+      - run: node scripts/prepublish-clean.mjs
       - uses: actions/upload-artifact@v4
         with:
           name: build-${{ github.run_id }}
@@ -46,12 +46,24 @@ jobs:
         with:
           node-version: 22
           cache: 'npm'
-      - uses: actions/download-artifact@v4
-        with:
-          name: build-${{ github.run_id }}
+
       - run: echo "//wombat-dressing-room.appspot.com/:_authToken=$AUTH_TOKEN" >> .npmrc
         env:
           AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
+
+      # publishing lite snapshot version: 1.2.3-lite-dev.abcd1234
+      - uses: actions/download-artifact@v4
+        with:
+          name: build-${{ github.run_id }}
+      - run: |
+          node scripts/prepublish-lite.mjs
+          npm version $(node --eval="process.stdout.write(require('./package.json').version)")-dev.$(git rev-parse --short HEAD) --no-git-tag-version
+          npm publish --provenance --access=public --no-git-tag-version --tag dev
+
+      # publishing regular snapshot version: 1.2.3-dev.abcd1234
+      - uses: actions/download-artifact@v4
+        with:
+          name: build-${{ github.run_id }}
       - run: |
           npm version $(node --eval="process.stdout.write(require('./package.json').version)")-dev.$(git rev-parse --short HEAD) --no-git-tag-version
           npm publish --provenance --access=public --no-git-tag-version --tag dev
.github/workflows/npm-publish.yml
@@ -22,7 +22,7 @@ jobs:
       - run: npm test
         env:
           FORCE_COLOR: 3
-      - run: node scripts/clean-package-json.mjs
+      - run: node scripts/prepublish-clean.mjs
       - uses: actions/upload-artifact@v4
         with:
           name: build-${{ github.run_id }}
@@ -54,4 +54,7 @@ jobs:
       - run: echo "//wombat-dressing-room.appspot.com/:_authToken=$AUTH_TOKEN" >> .npmrc
         env:
           AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
-      - run: npm publish --provenance --access=public
+      - run: |
+          npm publish --provenance --access=public
+          node scripts/prepublish-lite.mjs
+          npm publish --provenance --access=public --no-git-tag-version --tag lite
docs/.vitepress/config.mts
@@ -66,6 +66,7 @@ export default defineConfig({
             { text: 'CLI Usage', link: '/cli' },
             { text: 'Configuration', link: '/configuration' },
             { text: 'Process Promise', link: '/process-promise' },
+            { text: 'Process Output', link: '/process-output' },
             { text: 'Contribution Guide', link: '/contribution' },
             { text: 'Migration from v7', link: '/migration-from-v7' },
           ],
docs/process-output.md
@@ -3,7 +3,12 @@
 Represents a cmd execution result.
 
 ```ts
-interface ProcessOutput {
+const p = $`command` // ProcessPromise
+const o = await p     // ProcessOutput
+```
+
+```ts
+interface ProcessOutput extends Error {
   // Exit code of the process: 0 for success, non-zero for failure
   exitCode: number
   
@@ -15,6 +20,17 @@ interface ProcessOutput {
   
   // Process errors are written to stderr
   stderr: string
+
+  buffer(): Buffer
+
+  json<T = any>(): T
+
+  blob(type = 'text/plain'): Blob
+  
+  text(encoding: Encoding = 'utf8'): string
+
+  // Output lines splitted by newline
+  lines(): string[]
   
   // combined stdout and stderr
   toString(): string
docs/process-promise.md
@@ -1,6 +1,6 @@
 # Process Promise
 
-The `$` returns a `ProcessPromise` instance. When resolved, it becomes a [`ProcessOutput`](./process-output.md).
+The `$` returns a `ProcessPromise` instance, which inherits native `Promise`. When resolved, it becomes a [`ProcessOutput`](./process-output.md).
 
 ```js
 const p = $`command` // ProcessPromise
docs/setup.md
@@ -32,7 +32,8 @@ brew install zx
 
 :::
 
-Dev snapshot versions are published to npm under the [`dev` tag](https://www.npmjs.com/package/zx?activeTab=versions): `npm i zx@dev`.
+zx-core is distributed separately to the `lite` channel on npm: `npm i zx@lite`.  
+Dev snapshot versions are published to npm under the [`dev` tag](https://www.npmjs.com/package/zx?activeTab=versions): `npm i zx@dev`. See [versions](./versions) for more details.
 
 ## Bash
 
docs/versions.md
@@ -0,0 +1,53 @@
+# Versions
+
+zx is distributed in several versions, each with its own set of features.
+
+* `@latest` represents the stable full-featured version.
+* `@lite` separates the zx core from the extensions.
+* `@dev` brings experimental snapshots and RCs.
+
+| Feature           | latest | lite |
+|-------------------|--------|------|
+| **zx/globals**    | ✔️     | ️    |
+| **zx/cli**        | ✔️     |      |
+| `$`               | ✔️     | ✔️   |
+| `ProcessPromise`  | ✔️     | ✔️   |
+| `ProcessOutput`   | ✔️     | ✔️   |
+| `argv`            | ✔️     | ️    |
+| `cd`              | ✔️     | ✔️   |
+| `chalk`           | ✔️     | ✔️   |
+| `defaults`        | ✔️     | ✔️   |
+| `dotenv`          | ✔️     | ️    |
+| `echo`            | ✔️     | ️    |
+| `expBackoff`      | ✔️     | ️    |
+| `fetch`           | ✔️     | ️    |
+| `fs`              | ✔️     | ️    |
+| `glob`            | ✔️     | ️    |
+| `kill`            | ✔️     | ✔️   |
+| `log`             | ✔️     | ✔️   |
+| `minimist`        | ✔️     | ️    |
+| `nothrow`         | ✔️     | ️    |
+| `os`              | ✔️     | ✔️   |
+| `parseArgv`       | ✔️     | ️    |
+| `path`            | ✔️     | ✔️   |
+| `ps`              | ✔️     | ✔️   |
+| `question`        | ✔️     | ️    |
+| `quiet`           | ✔️     | ️    |
+| `quote`           | ✔️     | ✔️   |
+| `quotePowerShell` | ✔️     | ✔️   |
+| `resolveDefaults` | ✔️     | ✔️   |
+| `retry`           | ✔️     | ️    |
+| `sleep`           | ✔️     | ️    |
+| `spinner`         | ✔️     | ️    |
+| `syncProcessCwd`  | ✔️     | ✔️   |
+| `tempdir`         | ✔️     |      |
+| `tempfile`        | ✔️     |      |
+| `updateArgv`      | ✔️     |      |
+| `useBash`         | ✔️     | ✔️   |
+| `usePowerShell`   | ✔️     | ✔️   |
+| `usePwsh`         | ✔️     | ✔️   |
+| `version`         | ✔️     | ️    |
+| `which`           | ✔️     | ✔️   |
+| `whithin`         | ✔️     | ✔️   |
+| `YAML`            | ✔️     | ️    |
+
scripts/clean-package-json.mjs → scripts/prepublish-clean.mjs
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Optimizes package.json for npm publishing
+
 import fs from 'node:fs'
 import path from 'node:path'
 
@@ -46,3 +48,5 @@ const pkgJson = Object.fromEntries(
   Object.entries(_pkgJson).filter(([k]) => whitelist.has(k))
 )
 fs.writeFileSync(pkgJsonFile, JSON.stringify(pkgJson, null, 2))
+
+console.log('package.json optimized for npm publishing')
scripts/prepublish-lite.mjs
@@ -0,0 +1,62 @@
+// Copyright 2024 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.
+
+// Prepares lite (core) version of zx to publish
+
+import fs from 'node:fs'
+import path from 'node:path'
+
+const __dirname = path.dirname(new URL(import.meta.url).pathname)
+const root = path.resolve(__dirname, '..')
+const pkgJsonFile = path.join(root, 'package.json')
+const _pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf-8'))
+
+const pkgJson = {
+  ..._pkgJson,
+  version: _pkgJson.version + '-lite',
+  exports: {
+    '.': {
+      types: './build/core.d.ts',
+      import: './build/core.js',
+      require: './build/core.cjs',
+      default: './build/core.js',
+    },
+    './package.json': './package.json',
+  },
+  main: './build/core.cjs',
+  types: './build/core.d.ts',
+  typesVersions: {
+    '*': {
+      '.': ['./build/core.d.ts'],
+    },
+  },
+  man: undefined,
+  files: [
+    'build/core.cjs',
+    'build/core.js',
+    'build/core.d.ts',
+    'build/deno.js',
+    'build/esblib.js',
+    'build/util.cjs',
+    'build/util.js',
+    'build/util.d.ts',
+    'build/vendor-core.cjs',
+    'build/vendor-core.js',
+    'build/vendor-core.d.ts',
+  ],
+}
+
+fs.writeFileSync(pkgJsonFile, JSON.stringify(pkgJson, null, 2))
+
+console.log('package.json prepared for zx-lite publishing')
src/core.ts
@@ -63,7 +63,11 @@ import {
 
 import { log } from './log.ts'
 
+export { default as path } from 'node:path'
+export * as os from 'node:os'
 export { log, type LogEntry } from './log.ts'
+export { chalk, which, ps } from './vendor-core.ts'
+export { quote, quotePowerShell } from './util.ts'
 
 const CWD = Symbol('processCwd')
 const SYNC = Symbol('syncExec')
src/goods.ts
@@ -31,9 +31,6 @@ import {
   minimist,
 } from './vendor.ts'
 
-export { default as path } from 'node:path'
-export * as os from 'node:os'
-
 type ArgvOpts = minimist.Opts & { camelCase?: boolean; parseBoolean?: boolean }
 
 export const parseArgv = (
src/index.ts
@@ -17,17 +17,7 @@ import { fs } from './vendor.ts'
 
 export * from './core.ts'
 export * from './goods.ts'
-export {
-  minimist,
-  chalk,
-  dotenv,
-  fs,
-  which,
-  YAML,
-  ps,
-  glob,
-  glob as globby,
-} from './vendor.ts'
+export { minimist, dotenv, fs, YAML, glob, glob as globby } from './vendor.ts'
 
 export const VERSION: string = fs.readJsonSync(
   new URL('../package.json', import.meta.url)
test/core.test.js
@@ -1134,7 +1134,6 @@ describe('core', () => {
       assert.deepEqual(await p1.lines(), ['foo', 'bar', 'baz'])
 
       const p2 = $.sync`echo 'foo\nbar\r\nbaz'`
-      console.log('p2', p2)
       assert.deepEqual(p2.lines(), ['foo', 'bar', 'baz'])
     })
 
test/export.test.js
@@ -28,6 +28,8 @@ describe('core', () => {
     assert.equal(typeof core.ProcessOutput.getExitMessage, 'function', 'core.ProcessOutput.getExitMessage')
     assert.equal(typeof core.ProcessPromise, 'function', 'core.ProcessPromise')
     assert.equal(typeof core.cd, 'function', 'core.cd')
+    assert.equal(typeof core.chalk, 'function', 'core.chalk')
+    assert.equal(typeof core.chalk.level, 'number', 'core.chalk.level')
     assert.equal(typeof core.defaults, 'object', 'core.defaults')
     assert.equal(typeof core.defaults.detached, 'boolean', 'core.defaults.detached')
     assert.equal(typeof core.defaults.env, 'object', 'core.defaults.env')
@@ -49,11 +51,64 @@ describe('core', () => {
     assert.equal(typeof core.defaults.verbose, 'boolean', 'core.defaults.verbose')
     assert.equal(typeof core.kill, 'function', 'core.kill')
     assert.equal(typeof core.log, 'function', 'core.log')
+    assert.equal(typeof core.os, 'object', 'core.os')
+    assert.equal(typeof core.os.EOL, 'string', 'core.os.EOL')
+    assert.equal(typeof core.os.arch, 'function', 'core.os.arch')
+    assert.equal(typeof core.os.availableParallelism, 'function', 'core.os.availableParallelism')
+    assert.equal(typeof core.os.constants, 'object', 'core.os.constants')
+    assert.equal(typeof core.os.cpus, 'function', 'core.os.cpus')
+    assert.equal(typeof core.os.default, 'object', 'core.os.default')
+    assert.equal(typeof core.os.devNull, 'string', 'core.os.devNull')
+    assert.equal(typeof core.os.endianness, 'function', 'core.os.endianness')
+    assert.equal(typeof core.os.freemem, 'function', 'core.os.freemem')
+    assert.equal(typeof core.os.getPriority, 'function', 'core.os.getPriority')
+    assert.equal(typeof core.os.homedir, 'function', 'core.os.homedir')
+    assert.equal(typeof core.os.hostname, 'function', 'core.os.hostname')
+    assert.equal(typeof core.os.loadavg, 'function', 'core.os.loadavg')
+    assert.equal(typeof core.os.machine, 'function', 'core.os.machine')
+    assert.equal(typeof core.os.networkInterfaces, 'function', 'core.os.networkInterfaces')
+    assert.equal(typeof core.os.platform, 'function', 'core.os.platform')
+    assert.equal(typeof core.os.release, 'function', 'core.os.release')
+    assert.equal(typeof core.os.setPriority, 'function', 'core.os.setPriority')
+    assert.equal(typeof core.os.tmpdir, 'function', 'core.os.tmpdir')
+    assert.equal(typeof core.os.totalmem, 'function', 'core.os.totalmem')
+    assert.equal(typeof core.os.type, 'function', 'core.os.type')
+    assert.equal(typeof core.os.uptime, 'function', 'core.os.uptime')
+    assert.equal(typeof core.os.userInfo, 'function', 'core.os.userInfo')
+    assert.equal(typeof core.os.version, 'function', 'core.os.version')
+    assert.equal(typeof core.path, 'object', 'core.path')
+    assert.equal(typeof core.path._makeLong, 'function', 'core.path._makeLong')
+    assert.equal(typeof core.path.basename, 'function', 'core.path.basename')
+    assert.equal(typeof core.path.delimiter, 'string', 'core.path.delimiter')
+    assert.equal(typeof core.path.dirname, 'function', 'core.path.dirname')
+    assert.equal(typeof core.path.extname, 'function', 'core.path.extname')
+    assert.equal(typeof core.path.format, 'function', 'core.path.format')
+    assert.equal(typeof core.path.isAbsolute, 'function', 'core.path.isAbsolute')
+    assert.equal(typeof core.path.join, 'function', 'core.path.join')
+    assert.equal(typeof core.path.matchesGlob, 'function', 'core.path.matchesGlob')
+    assert.equal(typeof core.path.normalize, 'function', 'core.path.normalize')
+    assert.equal(typeof core.path.parse, 'function', 'core.path.parse')
+    assert.equal(typeof core.path.posix, 'object', 'core.path.posix')
+    assert.equal(typeof core.path.relative, 'function', 'core.path.relative')
+    assert.equal(typeof core.path.resolve, 'function', 'core.path.resolve')
+    assert.equal(typeof core.path.sep, 'string', 'core.path.sep')
+    assert.equal(typeof core.path.toNamespacedPath, 'function', 'core.path.toNamespacedPath')
+    assert.equal(typeof core.path.win32, 'object', 'core.path.win32')
+    assert.equal(typeof core.ps, 'object', 'core.ps')
+    assert.equal(typeof core.ps.kill, 'function', 'core.ps.kill')
+    assert.equal(typeof core.ps.lookup, 'function', 'core.ps.lookup')
+    assert.equal(typeof core.ps.lookupSync, 'function', 'core.ps.lookupSync')
+    assert.equal(typeof core.ps.tree, 'function', 'core.ps.tree')
+    assert.equal(typeof core.ps.treeSync, 'function', 'core.ps.treeSync')
+    assert.equal(typeof core.quote, 'function', 'core.quote')
+    assert.equal(typeof core.quotePowerShell, 'function', 'core.quotePowerShell')
     assert.equal(typeof core.resolveDefaults, 'function', 'core.resolveDefaults')
     assert.equal(typeof core.syncProcessCwd, 'function', 'core.syncProcessCwd')
     assert.equal(typeof core.useBash, 'function', 'core.useBash')
     assert.equal(typeof core.usePowerShell, 'function', 'core.usePowerShell')
     assert.equal(typeof core.usePwsh, 'function', 'core.usePwsh')
+    assert.equal(typeof core.which, 'function', 'core.which')
+    assert.equal(typeof core.which.sync, 'function', 'core.which.sync')
     assert.equal(typeof core.within, 'function', 'core.within')
   })
 })
.size-limit.json
@@ -1,22 +1,36 @@
 [
   {
-    "name": "zx/core",
-    "path": ["build/core.cjs", "build/util.cjs", "build/vendor-core.cjs"],
-    "limit": "77.3 kB",
+    "name": "zx-lite",
+    "path": [
+      "build/core.cjs",
+      "build/core.js",
+      "build/core.d.ts",
+      "build/deno.js",
+      "build/esblib.js",
+      "build/util.cjs",
+      "build/util.js",
+      "build/util.d.ts",
+      "build/vendor-core.cjs",
+      "build/vendor-core.js",
+      "build/vendor-core.d.ts",
+      "README.md",
+      "LICENSE"
+    ],
+    "limit": "111 kB",
     "brotli": false,
     "gzip": false
   },
   {
-    "name": "zx/index",
+    "name": "js parts",
     "path": "build/*.{js,cjs}",
-    "limit": "813 kB",
+    "limit": "813.5 kB",
     "brotli": false,
     "gzip": false
   },
   {
-    "name": "dts libdefs",
+    "name": "libdefs",
     "path": "build/*.d.ts",
-    "limit": "39.42 kB",
+    "limit": "40 kB",
     "brotli": false,
     "gzip": false
   },
@@ -29,8 +43,8 @@
   },
   {
     "name": "all",
-    "path": "build/*",
-    "limit": "852.5 kB",
+    "path": ["build/*", "man/*", "README.md", "LICENSE"],
+    "limit": "866.5 kB",
     "brotli": false,
     "gzip": false
   }