diff --git a/README.md b/README.md index 2fc637c..2d77ffe 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ import {semver} from 'zx-extra' semver.gte('1.0.1', '1.0.0') ``` +### $.preferLocal +In npm run scripts you can execute locally installed binaries by name. This enables the same for zx. +```js +$`terser input.js --compress ecma=2015,computed_props=false` +``` + ### `$.raw` Evaluates target cmd as is without `shq`. ```js diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4d48ddc..b303f01 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -9,8 +9,10 @@ "version": "1.2.0", "license": "MIT", "dependencies": { + "@qiwi/deep-proxy": "^1.9.0", "@types/node": "^17.0.38", "@types/semver": "^7.3.9", + "npm-run-path": "^5.1.0", "zx": "^6.2.0" }, "bin": { @@ -49,6 +51,14 @@ "node": ">= 8" } }, + "node_modules/@qiwi/deep-proxy": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@qiwi/deep-proxy/-/deep-proxy-1.9.0.tgz", + "integrity": "sha512-/lEv75Dck3UmXuGKdCE3aGk4KLRoEsT4yjPacmUDcKOChsMwamvXk9hlKn4uRzJuGG5XScOVM2FzNT6dYL9Ttw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -377,6 +387,31 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -514,6 +549,11 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -604,6 +644,14 @@ "fastq": "^1.6.0" } }, + "@qiwi/deep-proxy": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@qiwi/deep-proxy/-/deep-proxy-1.9.0.tgz", + "integrity": "sha512-/lEv75Dck3UmXuGKdCE3aGk4KLRoEsT4yjPacmUDcKOChsMwamvXk9hlKn4uRzJuGG5XScOVM2FzNT6dYL9Ttw==", + "requires": { + "tslib": "^2.4.0" + } + }, "@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -843,6 +891,19 @@ "formdata-polyfill": "^4.0.10" } }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "requires": { + "path-key": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -921,6 +982,11 @@ "is-number": "^7.0.0" } }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index 8d6ad35..f439a39 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "zx-extra": "./src/main/js/cli.mjs" }, "scripts": { - "test": "node ./src/main/js/cli.mjs ./src/test/js/test.mjs", + "test": "PATH=$(env -i bash -c 'echo $PATH') node ./src/main/js/cli.mjs ./src/test/js/test.mjs", "publish": "npm publish --no-git-tag-version", "publish:beta": "npm publish --no-git-tag-version --tag beta", "publish:rc": "npm publish --no-git-tag-version --tag rc" @@ -35,8 +35,10 @@ }, "homepage": "https://github.com/qiwi/zx-extra#readme", "dependencies": { + "@qiwi/deep-proxy": "^1.9.0", "@types/node": "^17.0.38", "@types/semver": "^7.3.9", + "npm-run-path": "^5.1.0", "zx": "^6.2.0" } } diff --git a/src/main/js/index.d.ts b/src/main/js/index.d.ts index 051e455..3e1f099 100644 --- a/src/main/js/index.d.ts +++ b/src/main/js/index.d.ts @@ -1,4 +1,5 @@ import * as semver from 'semver' +import {ProcessPromise} from 'zx' export * from 'zx' export { semver } @@ -6,4 +7,8 @@ export { semver } interface $ { raw: $ silent: $ + preferLocal?: boolean + opt: (options: any) => $ } + +export function createHook(opts?: $, name?: string, cb?: (p: ProcessPromise) => any, configurable?: boolean) diff --git a/src/main/js/index.mjs b/src/main/js/index.mjs index 2138d02..e5c2c50 100644 --- a/src/main/js/index.mjs +++ b/src/main/js/index.mjs @@ -1,10 +1,29 @@ -import {$, quiet, ProcessPromise} from 'zx' +import {$ as _$, quiet, ProcessPromise} from 'zx' import {ctx} from 'zx/experimental' import {isTemplateSignature, randomId} from './util.mjs' +import {npmRunPath} from 'npm-run-path' +import {DeepProxy} from '@qiwi/deep-proxy' export { semver } from './semver.mjs' export * from 'zx' +export const $ = new DeepProxy(_$, ({DEFAULT, trapName, args}) => { + if (trapName === 'apply') { + const [t,, receiver] = args + if (!t.preferLocal) { + return DEFAULT + } + const env = t.env + t.env = {...t.env, PATH: npmRunPath({cwd: t.cwd})} + const res = t(...receiver) + t.env = env + + return res + } + + return DEFAULT +}) + $.raw = async (...args) => { const q = $.quote $.quote = v => v diff --git a/src/test/js/test.mjs b/src/test/js/test.mjs index e24ee4a..421b235 100644 --- a/src/test/js/test.mjs +++ b/src/test/js/test.mjs @@ -89,3 +89,19 @@ import {$, semver, createHook} from '../../main/js/index.mjs' } } +// preferLocal +{ + $.verbose = 0 + try { + await $`ps-tree` + } catch (e){ + assert.ok(/command not found/.test(e.message)) + } + + $.preferLocal = true + + await $`ps-tree` + + $.verbose = 2 +} +