From 350a26f1d833e17862216b4087e35765f65e16af Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Mon, 3 Oct 2022 09:59:16 +0200 Subject: [PATCH] v0.8.0 - `run`/`runA` (#10) --- README.md | 23 +- _index.d.ts | 6 +- docs/README.md | 8 +- docs/classes/s.ProcessOutput.md | 265 ++++++++++++ docs/classes/s.ProcessPromise.md | 584 ++++++++++++++++++++++++++ docs/interfaces/cli.ReadOptions.md | 10 +- docs/interfaces/s.DollarFunction.md | 4 +- docs/interfaces/s.RunAsyncFunction.md | 69 +++ docs/interfaces/s.RunFunction.md | 106 +---- docs/interfaces/s.ShellReturnValue.md | 17 +- docs/interfaces/s.XargsFunction.md | 4 +- docs/interfaces/s.XargsOptions.md | 4 +- docs/modules/cli.md | 50 ++- docs/modules/s.md | 121 +++--- docs/modules/xdg_.md | 8 +- docs/modules/xdg_.xdg.md | 10 +- examples/background-process.mjs | 2 +- examples/cli.mjs | 4 +- examples/runA.mjs | 10 + package.json | 2 +- src/cli.d.ts | 9 + src/runA-utils.js | 214 ++++++++++ src/shelljs.d.ts | 125 +++--- src/shelljs.js | 73 ++-- 24 files changed, 1450 insertions(+), 278 deletions(-) create mode 100644 docs/classes/s.ProcessOutput.md create mode 100644 docs/classes/s.ProcessPromise.md create mode 100644 docs/interfaces/s.RunAsyncFunction.md create mode 100755 examples/runA.mjs create mode 100644 src/runA-utils.js diff --git a/README.md b/README.md index 6fce619..3a4c43e 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ const file_path= fileURLToPath(import.meta.url); …and more, see [Node.js v17.9.1 Documentation](https://nodejs.org/docs/latest-v17.x/api/documentation.html#stability-overview). ## Security guidelines -**`run()` command injection**: this advice applies to `child_process.exec()` just as +**`run()`/`runA()` command injection**: this advice applies to `child_process.exec()` just as much as it applies to `s.run()`. It is potentially risky to run commands passed for example by user input: ```js @@ -97,9 +97,15 @@ curlUnsafe('https://some/url ; rm -rf $HOME'); //=> curl https://some/url ; rm - ``` Therefore, `nodejsscript`s `s.run()` provide way to escapes untrusted parameters: ```js -function curl(url){ return s.run("run ::url::", { url }); } +function curl(url){ return s.run("curl ::url::", { url }); } curl('https://some/url ; rm -rf $HOME'); //=> curl 'https://some/url ; rm -rf $HOME' ``` +…you can also use as template function (but without command specific options): +```js +function curl(url){ return s.run`curl ${url}`; } +curl('https://some/url ; rm -rf $HOME'); //=> curl 'https://some/url ; rm -rf $HOME' +``` + …*Note: The ['xargs()'](../interfaces/s.XargsFunction.md) by default also escapes piped strings.* *…Note 2: `s.run(…cmd, …vars)` is also helpul for escaping parameters passed as variables (e.g. arrays).* @@ -116,6 +122,19 @@ Keep in mind that you can always turn off this for next command by using: s.$("-g").rm("*.txt"); ``` +## Migration from `zx` +The `runA` is almost identical to `$`: +```js +await $`cat package.json | grep name`; +await s.runA`cat package.json | grep name`; +``` +…but for `cp`/`mv`/… you need to rewrite code to `s.*`: +```js +echo(s.cat("package.json").grep("name")); +// or +echo(s.grep("name", "package.json")); +``` + ## Contribute - [Contributor Covenant Code of Conduc](./CODE_OF_CONDUCT.md) - [How to contribute](./CONTRIBUTING.md) diff --git a/_index.d.ts b/_index.d.ts index 5595222..42a7cb8 100644 --- a/_index.d.ts +++ b/_index.d.ts @@ -26,7 +26,7 @@ export function pipe(...funs: Function[]): (input: any)=> any; * · [find](https://github.com/shelljs/shelljs#findpath--path-) · [grep](https://github.com/shelljs/shelljs#grepoptions-regex_filter-file--file-) · [head](https://github.com/shelljs/shelljs#head-n-num-file--file-) · [ln](https://github.com/shelljs/shelljs#lnoptions-source-dest) * · [ls](https://github.com/shelljs/shelljs#lsoptions-path-) · [mkdir](https://github.com/shelljs/shelljs#mkdiroptions-dir--dir-) · [mv](https://github.com/shelljs/shelljs#mvoptions--source--source--dest) · [pwd](https://github.com/shelljs/shelljs#pwd) * · [rm](https://github.com/shelljs/shelljs#rmoptions-file--file-) · [sed](https://github.com/shelljs/shelljs#sedoptions-search_regex-replacement-file--file-) · [sort](https://github.com/shelljs/shelljs#sortoptions-file--file-) - * · [tail](https://github.com/shelljs/shelljs#tail-n-num-file--file-) · [tempdir](https://github.com/shelljs/shelljs#tempdir) · [test](https://github.com/shelljs/shelljs#testexpression) · [touch](https://github.com/shelljs/shelljs#touchoptions-file--file-) + * · [tail](https://github.com/shelljs/shelljs#tail-n-num-file--file-) · [test](https://github.com/shelljs/shelljs#testexpression) · [touch](https://github.com/shelljs/shelljs#touchoptions-file--file-) * · [uniq](https://github.com/shelljs/shelljs#uniqoptions-input-output) · [which](https://github.com/shelljs/shelljs#whichcommand) · [exit](https://github.com/shelljs/shelljs#exitcode) · [error](https://github.com/shelljs/shelljs#error) · [errorCode](https://github.com/shelljs/shelljs#errorcode) * * ```js @@ -34,13 +34,15 @@ export function pipe(...funs: Function[]): (input: any)=> any; * ``` * … this library adds: * - {@link s.RunFunction 'run()'} + * - {@link s.RunAsyncFunction 'runA()'} * - {@link s.XargsFunction 'xargs()'} * - {@link s.DollarFunction '$()'} * * **Changes/recommenctions:** * - use {@link echo} instead of `s.echo`, this was changed to `s.ShellString` for easy file writing without logging to console `s.echo("Data").to("file.txt")`. - * - use {@link s.RunFunction 'run()'} instead of `s.exec`, because of options for passing arguments in secure way. + * - use {@link s.RunFunction 'run()'}/{@link s.RunAsyncFunction 'runA()'} instead of `s.exec`, because of options for passing arguments in secure way. * - use {@link s.DollarFunction '$()'} instead of `s.set()`, because `$()` allows chaining (you can also access config with {@link cli}s `.is_*` keys). + * - use {@link cli.xdg}`.temp` instead of `s.tempdir()` – the `cli.xdg.*` provides more paths than just temp directory. * @category Public */ export * as s from './src/shelljs.d'; diff --git a/docs/README.md b/docs/README.md index f704257..6e7a65e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -68,7 +68,7 @@ pipe( #### Defined in -[_index.d.ts:18](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/_index.d.ts#L18) +[_index.d.ts:18](https://github.com/jaandrle/nodejsscript/blob/6b875ec/_index.d.ts#L18) ___ @@ -116,7 +116,7 @@ try{ #### Defined in -[_index.d.ts:92](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/_index.d.ts#L92) +[_index.d.ts:94](https://github.com/jaandrle/nodejsscript/blob/6b875ec/_index.d.ts#L94) ___ @@ -162,7 +162,7 @@ function spinner(message= "Waiting…"){ #### Defined in -[_index.d.ts:117](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/_index.d.ts#L117) +[_index.d.ts:119](https://github.com/jaandrle/nodejsscript/blob/6b875ec/_index.d.ts#L119) ___ @@ -217,7 +217,7 @@ Returns processed string with additional utility methods like .to(). #### Defined in -[src/echo.d.ts:46](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/echo.d.ts#L46) +[src/echo.d.ts:46](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/echo.d.ts#L46) ___ diff --git a/docs/classes/s.ProcessOutput.md b/docs/classes/s.ProcessOutput.md new file mode 100644 index 0000000..5ceae06 --- /dev/null +++ b/docs/classes/s.ProcessOutput.md @@ -0,0 +1,265 @@ +[nodejsscript](../README.md) / [s](../modules/s.md) / ProcessOutput + +# Class: ProcessOutput + +[s](../modules/s.md).ProcessOutput + +## Hierarchy + +- `Error` + + ↳ **`ProcessOutput`** + +## Table of contents + +### Methods + +- [captureStackTrace](s.ProcessOutput.md#capturestacktrace) +- [toString](s.ProcessOutput.md#tostring) +- [[custom]](s.ProcessOutput.md#[custom]) + +### Properties + +- [prepareStackTrace](s.ProcessOutput.md#preparestacktrace) +- [stackTraceLimit](s.ProcessOutput.md#stacktracelimit) +- [name](s.ProcessOutput.md#name) +- [message](s.ProcessOutput.md#message) +- [stack](s.ProcessOutput.md#stack) + +### Constructors + +- [constructor](s.ProcessOutput.md#constructor) + +### Accessors + +- [stdout](s.ProcessOutput.md#stdout) +- [stderr](s.ProcessOutput.md#stderr) +- [exitCode](s.ProcessOutput.md#exitcode) +- [signal](s.ProcessOutput.md#signal) + +## Methods + +### captureStackTrace + +▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` + +Create .stack property on a target object + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `targetObject` | `object` | +| `constructorOpt?` | `Function` | + +#### Returns + +`void` + +#### Inherited from + +Error.captureStackTrace + +#### Defined in + +node_modules/@types/node/ts4.8/globals.d.ts:4 + +___ + +### toString + +▸ **toString**(): `string` + +#### Returns + +`string` + +#### Defined in + +[src/shelljs.d.ts:112](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L112) + +___ + +### [custom] + +▸ **[custom]**(): `string` + +#### Returns + +`string` + +#### Defined in + +[src/shelljs.d.ts:117](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L117) + +## Properties + +### prepareStackTrace + +▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` + +#### Type declaration + +▸ (`err`, `stackTraces`): `any` + +Optional override for formatting stack traces + +**`See`** + +https://v8.dev/docs/stack-trace-api#customizing-stack-traces + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `err` | `Error` | +| `stackTraces` | `CallSite`[] | + +##### Returns + +`any` + +#### Inherited from + +Error.prepareStackTrace + +#### Defined in + +node_modules/@types/node/ts4.8/globals.d.ts:11 + +___ + +### stackTraceLimit + +▪ `Static` **stackTraceLimit**: `number` + +#### Inherited from + +Error.stackTraceLimit + +#### Defined in + +node_modules/@types/node/ts4.8/globals.d.ts:13 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +Error.name + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1040 + +___ + +### message + +• **message**: `string` + +#### Inherited from + +Error.message + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1041 + +___ + +### stack + +• `Optional` **stack**: `string` + +#### Inherited from + +Error.stack + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1042 + +## Constructors + +### constructor + +• **new ProcessOutput**(`code`, `signal`, `stdout`, `stderr`, `combined`, `message`) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `code` | `number` | +| `signal` | `Signals` | +| `stdout` | `string` | +| `stderr` | `string` | +| `combined` | `string` | +| `message` | `string` | + +#### Overrides + +Error.constructor + +#### Defined in + +[src/shelljs.d.ts:111](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L111) + +## Accessors + +### stdout + +• `get` **stdout**(): `string` + +#### Returns + +`string` + +#### Defined in + +[src/shelljs.d.ts:113](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L113) + +___ + +### stderr + +• `get` **stderr**(): `string` + +#### Returns + +`string` + +#### Defined in + +[src/shelljs.d.ts:114](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L114) + +___ + +### exitCode + +• `get` **exitCode**(): `number` + +#### Returns + +`number` + +#### Defined in + +[src/shelljs.d.ts:115](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L115) + +___ + +### signal + +• `get` **signal**(): `Signals` + +#### Returns + +`Signals` + +#### Defined in + +[src/shelljs.d.ts:116](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L116) diff --git a/docs/classes/s.ProcessPromise.md b/docs/classes/s.ProcessPromise.md new file mode 100644 index 0000000..3e9eae5 --- /dev/null +++ b/docs/classes/s.ProcessPromise.md @@ -0,0 +1,584 @@ +[nodejsscript](../README.md) / [s](../modules/s.md) / ProcessPromise + +# Class: ProcessPromise + +[s](../modules/s.md).ProcessPromise + +## Hierarchy + +- `Promise`<[`ProcessOutput`](s.ProcessOutput.md)\> + + ↳ **`ProcessPromise`** + +## Table of contents + +### Methods + +- [all](s.ProcessPromise.md#all) +- [race](s.ProcessPromise.md#race) +- [reject](s.ProcessPromise.md#reject) +- [resolve](s.ProcessPromise.md#resolve) +- [allSettled](s.ProcessPromise.md#allsettled) +- [\_run](s.ProcessPromise.md#_run) +- [stdio](s.ProcessPromise.md#stdio) +- [pipe](s.ProcessPromise.md#pipe) +- [then](s.ProcessPromise.md#then) +- [catch](s.ProcessPromise.md#catch) +- [finally](s.ProcessPromise.md#finally) + +### Properties + +- [[species]](s.ProcessPromise.md#[species]) +- [child](s.ProcessPromise.md#child) +- [[toStringTag]](s.ProcessPromise.md#[tostringtag]) + +### Constructors + +- [constructor](s.ProcessPromise.md#constructor) + +### Accessors + +- [stdin](s.ProcessPromise.md#stdin) +- [stdout](s.ProcessPromise.md#stdout) +- [stderr](s.ProcessPromise.md#stderr) + +## Methods + +### all + +▸ `Static` **all**<`T`\>(`values`): `Promise`<`Awaited`<`T`\>[]\> + +Creates a Promise that is resolved with an array of results when all of the provided Promises +resolve, or rejected when any Promise is rejected. + +#### Type parameters + +| Name | +| :------ | +| `T` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `Iterable`<`T` \| `PromiseLike`<`T`\>\> | An iterable of Promises. | + +#### Returns + +`Promise`<`Awaited`<`T`\>[]\> + +A new Promise. + +#### Inherited from + +Promise.all + +#### Defined in + +node_modules/typescript/lib/lib.es2015.iterable.d.ts:227 + +▸ `Static` **all**<`T`\>(`values`): `Promise`<{ -readonly [P in string \| number \| symbol]: Awaited }\> + +Creates a Promise that is resolved with an array of results when all of the provided Promises +resolve, or rejected when any Promise is rejected. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [] \| readonly `unknown`[] | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `T` | An array of Promises. | + +#### Returns + +`Promise`<{ -readonly [P in string \| number \| symbol]: Awaited }\> + +A new Promise. + +#### Inherited from + +Promise.all + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:41 + +___ + +### race + +▸ `Static` **race**<`T`\>(`values`): `Promise`<`Awaited`<`T`\>\> + +Creates a Promise that is resolved or rejected when any of the provided Promises are resolved +or rejected. + +#### Type parameters + +| Name | +| :------ | +| `T` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `Iterable`<`T` \| `PromiseLike`<`T`\>\> | An iterable of Promises. | + +#### Returns + +`Promise`<`Awaited`<`T`\>\> + +A new Promise. + +#### Inherited from + +Promise.race + +#### Defined in + +node_modules/typescript/lib/lib.es2015.iterable.d.ts:235 + +▸ `Static` **race**<`T`\>(`values`): `Promise`<`Awaited`<`T`[`number`]\>\> + +Creates a Promise that is resolved or rejected when any of the provided Promises are resolved +or rejected. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [] \| readonly `unknown`[] | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `T` | An array of Promises. | + +#### Returns + +`Promise`<`Awaited`<`T`[`number`]\>\> + +A new Promise. + +#### Inherited from + +Promise.race + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:52 + +___ + +### reject + +▸ `Static` **reject**<`T`\>(`reason?`): `Promise`<`T`\> + +Creates a new rejected promise for the provided reason. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | `never` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `reason?` | `any` | The reason the promise was rejected. | + +#### Returns + +`Promise`<`T`\> + +A new rejected Promise. + +#### Inherited from + +Promise.reject + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:62 + +___ + +### resolve + +▸ `Static` **resolve**(): `Promise`<`void`\> + +Creates a new resolved promise. + +#### Returns + +`Promise`<`void`\> + +A resolved promise. + +#### Inherited from + +Promise.resolve + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:68 + +▸ `Static` **resolve**<`T`\>(`value`): `Promise`<`T`\> + +Creates a new resolved promise for the provided value. + +#### Type parameters + +| Name | +| :------ | +| `T` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `value` | `T` \| `PromiseLike`<`T`\> | A promise. | + +#### Returns + +`Promise`<`T`\> + +A promise whose internal state matches the provided promise. + +#### Inherited from + +Promise.resolve + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:75 + +___ + +### allSettled + +▸ `Static` **allSettled**<`T`\>(`values`): `Promise`<{ -readonly [P in string \| number \| symbol]: PromiseSettledResult\> }\> + +Creates a Promise that is resolved with an array of results when all +of the provided Promises resolve or reject. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [] \| readonly `unknown`[] | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `T` | An array of Promises. | + +#### Returns + +`Promise`<{ -readonly [P in string \| number \| symbol]: PromiseSettledResult\> }\> + +A new Promise. + +#### Inherited from + +Promise.allSettled + +#### Defined in + +node_modules/typescript/lib/lib.es2020.promise.d.ts:40 + +▸ `Static` **allSettled**<`T`\>(`values`): `Promise`<`PromiseSettledResult`<`Awaited`<`T`\>\>[]\> + +Creates a Promise that is resolved with an array of results when all +of the provided Promises resolve or reject. + +#### Type parameters + +| Name | +| :------ | +| `T` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `values` | `Iterable`<`T` \| `PromiseLike`<`T`\>\> | An array of Promises. | + +#### Returns + +`Promise`<`PromiseSettledResult`<`Awaited`<`T`\>\>[]\> + +A new Promise. + +#### Inherited from + +Promise.allSettled + +#### Defined in + +node_modules/typescript/lib/lib.es2020.promise.d.ts:48 + +___ + +### \_run + +▸ **_run**(): `void` + +#### Returns + +`void` + +#### Defined in + +[src/shelljs.d.ts:121](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L121) + +___ + +### stdio + +▸ **stdio**(`stdin`, `stdout?`, `stderr?`): [`ProcessPromise`](s.ProcessPromise.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `stdin` | [`IO`](../modules/s.md#io) | +| `stdout?` | [`IO`](../modules/s.md#io) | +| `stderr?` | [`IO`](../modules/s.md#io) | + +#### Returns + +[`ProcessPromise`](s.ProcessPromise.md) + +#### Defined in + +[src/shelljs.d.ts:125](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L125) + +___ + +### pipe + +▸ **pipe**(`dest`): [`ProcessPromise`](s.ProcessPromise.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `dest` | `Writable` \| [`ProcessPromise`](s.ProcessPromise.md) \| (`s`: [`ShellString`](../modules/s.md#shellstring)) => `any` | + +#### Returns + +[`ProcessPromise`](s.ProcessPromise.md) + +#### Defined in + +[src/shelljs.d.ts:126](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L126) + +___ + +### then + +▸ **then**<`TResult1`, `TResult2`\>(`onfulfilled?`, `onrejected?`): `Promise`<`TResult1` \| `TResult2`\> + +Attaches callbacks for the resolution and/or rejection of the Promise. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `TResult1` | [`ProcessOutput`](s.ProcessOutput.md) | +| `TResult2` | `never` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `onfulfilled?` | (`value`: [`ProcessOutput`](s.ProcessOutput.md)) => `TResult1` \| `PromiseLike`<`TResult1`\> | The callback to execute when the Promise is resolved. | +| `onrejected?` | (`reason`: `any`) => `TResult2` \| `PromiseLike`<`TResult2`\> | The callback to execute when the Promise is rejected. | + +#### Returns + +`Promise`<`TResult1` \| `TResult2`\> + +A Promise for the completion of which ever callback is executed. + +#### Inherited from + +Promise.then + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1520 + +___ + +### catch + +▸ **catch**<`TResult`\>(`onrejected?`): `Promise`<[`ProcessOutput`](s.ProcessOutput.md) \| `TResult`\> + +Attaches a callback for only the rejection of the Promise. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `TResult` | `never` | + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `onrejected?` | (`reason`: `any`) => `TResult` \| `PromiseLike`<`TResult`\> | The callback to execute when the Promise is rejected. | + +#### Returns + +`Promise`<[`ProcessOutput`](s.ProcessOutput.md) \| `TResult`\> + +A Promise for the completion of the callback. + +#### Inherited from + +Promise.catch + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1527 + +___ + +### finally + +▸ **finally**(`onfinally?`): `Promise`<[`ProcessOutput`](s.ProcessOutput.md)\> + +Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The +resolved value cannot be modified from the callback. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `onfinally?` | () => `void` | The callback to execute when the Promise is settled (fulfilled or rejected). | + +#### Returns + +`Promise`<[`ProcessOutput`](s.ProcessOutput.md)\> + +A Promise for the completion of the callback. + +#### Inherited from + +Promise.finally + +#### Defined in + +node_modules/typescript/lib/lib.es2018.promise.d.ts:31 + +## Properties + +### [species] + +▪ `Static` `Readonly` **[species]**: `PromiseConstructor` + +#### Inherited from + +Promise.\_\_@species@596 + +#### Defined in + +node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts:178 + +___ + +### child + +• `Optional` **child**: [`ChildProcess`](s.child.ChildProcess.md) + +#### Defined in + +[src/shelljs.d.ts:120](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L120) + +___ + +### [toStringTag] + +• `Readonly` **[toStringTag]**: `string` + +#### Inherited from + +Promise.\_\_@toStringTag@23 + +#### Defined in + +node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts:174 + +## Constructors + +### constructor + +• **new ProcessPromise**(`executor`) + +Creates a new Promise. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `executor` | (`resolve`: (`value`: [`ProcessOutput`](s.ProcessOutput.md) \| `PromiseLike`<[`ProcessOutput`](s.ProcessOutput.md)\>) => `void`, `reject`: (`reason?`: `any`) => `void`) => `void` | A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error. | + +#### Inherited from + +Promise.constructor + +#### Defined in + +node_modules/typescript/lib/lib.es2015.promise.d.ts:33 + +## Accessors + +### stdin + +• `get` **stdin**(): `Writable` + +#### Returns + +`Writable` + +#### Defined in + +[src/shelljs.d.ts:122](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L122) + +___ + +### stdout + +• `get` **stdout**(): `Readable` + +#### Returns + +`Readable` + +#### Defined in + +[src/shelljs.d.ts:123](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L123) + +___ + +### stderr + +• `get` **stderr**(): `Readable` + +#### Returns + +`Readable` + +#### Defined in + +[src/shelljs.d.ts:124](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L124) diff --git a/docs/interfaces/cli.ReadOptions.md b/docs/interfaces/cli.ReadOptions.md index c393d59..258a2fc 100644 --- a/docs/interfaces/cli.ReadOptions.md +++ b/docs/interfaces/cli.ReadOptions.md @@ -24,7 +24,7 @@ Promt mode, value is used as question. It is possible to cobine with other optio #### Defined in -[src/cli.d.ts:114](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L114) +[src/cli.d.ts:114](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L114) ___ @@ -36,7 +36,7 @@ Make sence to combine only with `-p` to not show pressed keys (e.g. to prompt pa #### Defined in -[src/cli.d.ts:116](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L116) +[src/cli.d.ts:116](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L116) ___ @@ -48,7 +48,7 @@ Make sence to combine only with `-p` to provide tab suggestion/completions. #### Defined in -[src/cli.d.ts:118](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L118) +[src/cli.d.ts:118](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L118) ___ @@ -60,7 +60,7 @@ Returns the `stdin` till given needle. #### Defined in -[src/cli.d.ts:120](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L120) +[src/cli.d.ts:120](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L120) ___ @@ -72,4 +72,4 @@ Choose given number of chars from `stdin`. #### Defined in -[src/cli.d.ts:122](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L122) +[src/cli.d.ts:122](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L122) diff --git a/docs/interfaces/s.DollarFunction.md b/docs/interfaces/s.DollarFunction.md index 3a95c83..65f421a 100644 --- a/docs/interfaces/s.DollarFunction.md +++ b/docs/interfaces/s.DollarFunction.md @@ -41,7 +41,7 @@ s.$("-g").rm("*.tx"); //remove only "*.txt" file #### Defined in -[src/shelljs.d.ts:54](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L54) +[src/shelljs.d.ts:54](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L54) ### DollarFunction @@ -53,4 +53,4 @@ s.$("-g").rm("*.tx"); //remove only "*.txt" file #### Defined in -[src/shelljs.d.ts:55](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L55) +[src/shelljs.d.ts:55](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L55) diff --git a/docs/interfaces/s.RunAsyncFunction.md b/docs/interfaces/s.RunAsyncFunction.md new file mode 100644 index 0000000..85af228 --- /dev/null +++ b/docs/interfaces/s.RunAsyncFunction.md @@ -0,0 +1,69 @@ +[nodejsscript](../README.md) / [s](../modules/s.md) / RunAsyncFunction + +# Interface: RunAsyncFunction + +[s](../modules/s.md).RunAsyncFunction + +## Callable + +### RunAsyncFunction + +▸ **RunAsyncFunction**(`command`, `vars`): [`ProcessPromise`](../classes/s.ProcessPromise.md) + +Executes the given command asynchronously. +```js +s.$().runA("git branch --show-current") +.then(echo.bind(echo, "success:")) +.catch(echo.bind(echo, "error:")); + +s.$().runA("npm list") +.pipe(s=> echo(s.grep("types"))) +.catch(echo.bind(echo, "error:")); + +const ch= s.$().runA("git branch --show-current"); +ch.child.on("data", echo); + +const result_a= await s.$().runA("git branch --show-current"); +echo(result_a.toString()); +``` + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | +| `vars` | ``false`` \| {} | Arguments for `command`. | + +#### Returns + +[`ProcessPromise`](../classes/s.ProcessPromise.md) + +#### Defined in + +[src/shelljs.d.ts:152](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L152) + +### RunAsyncFunction + +▸ **RunAsyncFunction**(`command`, `vars`, `options`): [`ProcessPromise`](../classes/s.ProcessPromise.md) + +Executes the given command asynchronously. +```js +const result_b= await s.$().runA("git branch --show-::var::", { var: "current" }, { silent: true }); +echo(result_b.toString()); +``` + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | +| `vars` | ``false`` \| {} | Arguments for `command`. | +| `options` | [`RunOptions`](../modules/s.md#runoptions) | Silence and options. | + +#### Returns + +[`ProcessPromise`](../classes/s.ProcessPromise.md) + +#### Defined in + +[src/shelljs.d.ts:165](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L165) diff --git a/docs/interfaces/s.RunFunction.md b/docs/interfaces/s.RunFunction.md index 4c44354..00dbc67 100644 --- a/docs/interfaces/s.RunFunction.md +++ b/docs/interfaces/s.RunFunction.md @@ -10,7 +10,8 @@ ▸ **RunFunction**(`command`, `vars?`): [`ShellString`](../modules/s.md#shellstring) -Executes the given command synchronously. +Executes the given command synchronously, because of that it does not know whether it will be piped, +so by default prints the command output. You can off that by prepend `….$().run`. *Synchronous simple examples*: ```js @@ -34,17 +35,18 @@ s.run("echo ::branch::", { branch }); [`ShellString`](../modules/s.md#shellstring) -Returns an object containing the return code and output as string. +Returns an object containing the return code and output as [ShellString](../modules/s.md#shellstring). #### Defined in -[src/shelljs.d.ts:94](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L94) +[src/shelljs.d.ts:87](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L87) ### RunFunction ▸ **RunFunction**(`command`, `vars`, `options`): [`ShellString`](../modules/s.md#shellstring) -Executes the given command synchronously. +Executes the given command synchronously, because of that it does not know whether it will be piped, +so by default prints the command output. You can off that by prepend `….$().run`. *Passing variables*: ```js @@ -58,104 +60,14 @@ s.run("echo ::branch::", { branch }); | :------ | :------ | :------ | | `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | | `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | [`ExecOptions`](s.ExecOptions.md) & { `async?`: `boolean` \| ``"child"`` ; `needle?`: `RegExp` } & { `async?`: ``false`` } | Silence and synchronous options. | +| `options` | [`RunOptions`](../modules/s.md#runoptions) | Silence and options. | #### Returns [`ShellString`](../modules/s.md#shellstring) -Returns an object containing the return code and output as string, - or if `{async: true}` was passed, a `ChildProcess`. +Returns an object containing the return code and output as [ShellString](../modules/s.md#shellstring). #### Defined in -[src/shelljs.d.ts:111](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L111) - -### RunFunction - -▸ **RunFunction**(`command`, `vars`, `options`): `Promise`<`string`\> - -Executes the given command asynchronously. -```js -s.$().run("git branch --show-current", false, { async: true }) -.then(echo.bind(echo, "success:")) -.catch(echo.bind(echo, "error:")) -``` - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | -| `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | [`ExecOptions`](s.ExecOptions.md) & { `async?`: `boolean` \| ``"child"`` ; `needle?`: `RegExp` } & { `async`: ``true`` } | Silence and synchronous options. | - -#### Returns - -`Promise`<`string`\> - -Returns an object containing the return code and output as string, - or if `{async: true}` was passed, a `Promise`. - -#### Defined in - -[src/shelljs.d.ts:127](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L127) - -### RunFunction - -▸ **RunFunction**(`command`, `vars?`, `options?`): `Promise`<`string`\> - -Executes the given command asynchronously. -```js -s.$().run("git branch --show-current", false, { async: true }) -.then(echo.bind(echo, "success:")) -.catch(echo.bind(echo, "error:")) -``` - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `command` | \`${string} &\` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | -| `vars?` | ``false`` \| {} | Arguments for `command`. | -| `options?` | [`RunOptions`](../modules/s.md#runoptions) | Silence and synchronous options. | - -#### Returns - -`Promise`<`string`\> - -Returns an object containing the return code and output as string, - or if `{async: true}` was passed, a `Promise`. - -#### Defined in - -[src/shelljs.d.ts:143](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L143) - -### RunFunction - -▸ **RunFunction**(`command`, `vars`, `options`): [`ChildProcess`](../classes/s.child.ChildProcess.md) - -Executes the given command asynchronously. *Get the [child](../modules/s.child.md)*: -```js -const ch= s.$().run("git branch --show-current", false, { async: "child" }); -ch.on("data", echo); -``` - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | -| `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | `never` | Silence and synchronous options. | - -#### Returns - -[`ChildProcess`](../classes/s.child.ChildProcess.md) - -Returns an object containing the return code and output as string, - or if `{async: "child"}` was passed, a `ChildProcess`. - -#### Defined in - -[src/shelljs.d.ts:158](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L158) +[src/shelljs.d.ts:104](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L104) diff --git a/docs/interfaces/s.ShellReturnValue.md b/docs/interfaces/s.ShellReturnValue.md index 1cf97a1..af983ac 100644 --- a/docs/interfaces/s.ShellReturnValue.md +++ b/docs/interfaces/s.ShellReturnValue.md @@ -11,6 +11,7 @@ - [xargs](s.ShellReturnValue.md#xargs) - [$](s.ShellReturnValue.md#$) - [run](s.ShellReturnValue.md#run) +- [runA](s.ShellReturnValue.md#runa) ## Properties @@ -20,7 +21,7 @@ #### Defined in -[src/shelljs.d.ts:172](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L172) +[src/shelljs.d.ts:202](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L202) ___ @@ -30,7 +31,7 @@ ___ #### Defined in -[src/shelljs.d.ts:173](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L173) +[src/shelljs.d.ts:203](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L203) ___ @@ -40,4 +41,14 @@ ___ #### Defined in -[src/shelljs.d.ts:174](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L174) +[src/shelljs.d.ts:204](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L204) + +___ + +### runA + +• **runA**: [`RunAsyncFunction`](s.RunAsyncFunction.md) + +#### Defined in + +[src/shelljs.d.ts:205](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L205) diff --git a/docs/interfaces/s.XargsFunction.md b/docs/interfaces/s.XargsFunction.md index 6b17d8e..d8bbb6b 100644 --- a/docs/interfaces/s.XargsFunction.md +++ b/docs/interfaces/s.XargsFunction.md @@ -39,7 +39,7 @@ Result of `cmd` #### Defined in -[src/shelljs.d.ts:25](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L25) +[src/shelljs.d.ts:25](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L25) ### XargsFunction @@ -64,4 +64,4 @@ Result of `cmd` #### Defined in -[src/shelljs.d.ts:26](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L26) +[src/shelljs.d.ts:26](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L26) diff --git a/docs/interfaces/s.XargsOptions.md b/docs/interfaces/s.XargsOptions.md index e2bbb33..56f035f 100644 --- a/docs/interfaces/s.XargsOptions.md +++ b/docs/interfaces/s.XargsOptions.md @@ -21,7 +21,7 @@ Next parameter represents to be replaced in `cmd_args`. #### Defined in -[src/shelljs.d.ts:8](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L8) +[src/shelljs.d.ts:8](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L8) ___ @@ -33,4 +33,4 @@ Raw piped string ⇒ turn off escaping piped string (by default). #### Defined in -[src/shelljs.d.ts:10](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L10) +[src/shelljs.d.ts:10](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L10) diff --git a/docs/modules/cli.md b/docs/modules/cli.md index a04e794..04e7d6d 100644 --- a/docs/modules/cli.md +++ b/docs/modules/cli.md @@ -11,6 +11,10 @@ - [api](cli.md#api) - [read](cli.md#read) +### Internal Functions + +- [error](cli.md#error) + ### Public Variables - [is\_silent](cli.md#is_silent) @@ -48,7 +52,7 @@ const cli.configAssign({ verbose: true, silent: false }); #### Defined in -[src/cli.d.ts:53](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L53) +[src/cli.d.ts:53](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L53) ___ @@ -74,7 +78,7 @@ node pipes.js | … # — test by cli.isFIFO(1) #### Defined in -[src/cli.d.ts:63](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L63) +[src/cli.d.ts:63](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L63) ___ @@ -137,7 +141,7 @@ prog.parse(process.argv); #### Defined in -[src/cli.d.ts:110](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L110) +[src/cli.d.ts:110](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L110) ___ @@ -165,7 +169,35 @@ if(cli.isFIFO(0)) await cli.read().then(echo.bind(null, "E.g. for reading receiv #### Defined in -[src/cli.d.ts:134](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L134) +[src/cli.d.ts:134](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L134) + +___ + +## Internal Functions + +### error + +▸ **error**(`message`): `Error` + +Throws user targeted error +```js +const number= await cli.read({ "-p". "Insert a number:" }); +if(Number.isNaN(Number(number))) cli.error(`Provided text '${number}' is not a number`); +``` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message` | `string` | + +#### Returns + +`Error` + +#### Defined in + +[src/cli.d.ts:148](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L148) ## Public Variables @@ -181,7 +213,7 @@ false #### Defined in -[src/cli.d.ts:16](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L16) +[src/cli.d.ts:16](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L16) ___ @@ -197,7 +229,7 @@ false #### Defined in -[src/cli.d.ts:22](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L22) +[src/cli.d.ts:22](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L22) ___ @@ -213,7 +245,7 @@ false #### Defined in -[src/cli.d.ts:28](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L28) +[src/cli.d.ts:28](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L28) ___ @@ -230,7 +262,7 @@ ___ #### Defined in -[src/cli.d.ts:31](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L31) +[src/cli.d.ts:31](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L31) ___ @@ -240,4 +272,4 @@ ___ #### Defined in -[src/cli.d.ts:139](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/cli.d.ts#L139) +[src/cli.d.ts:139](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/cli.d.ts#L139) diff --git a/docs/modules/s.md b/docs/modules/s.md index 5bf4a1b..2caa396 100644 --- a/docs/modules/s.md +++ b/docs/modules/s.md @@ -10,7 +10,7 @@ Available commands: [cat](https://github.com/shelljs/shelljs#catoptions-file--fi · [find](https://github.com/shelljs/shelljs#findpath--path-) · [grep](https://github.com/shelljs/shelljs#grepoptions-regex_filter-file--file-) · [head](https://github.com/shelljs/shelljs#head-n-num-file--file-) · [ln](https://github.com/shelljs/shelljs#lnoptions-source-dest) · [ls](https://github.com/shelljs/shelljs#lsoptions-path-) · [mkdir](https://github.com/shelljs/shelljs#mkdiroptions-dir--dir-) · [mv](https://github.com/shelljs/shelljs#mvoptions--source--source--dest) · [pwd](https://github.com/shelljs/shelljs#pwd) · [rm](https://github.com/shelljs/shelljs#rmoptions-file--file-) · [sed](https://github.com/shelljs/shelljs#sedoptions-search_regex-replacement-file--file-) · [sort](https://github.com/shelljs/shelljs#sortoptions-file--file-) - · [tail](https://github.com/shelljs/shelljs#tail-n-num-file--file-) · [tempdir](https://github.com/shelljs/shelljs#tempdir) · [test](https://github.com/shelljs/shelljs#testexpression) · [touch](https://github.com/shelljs/shelljs#touchoptions-file--file-) + · [tail](https://github.com/shelljs/shelljs#tail-n-num-file--file-) · [test](https://github.com/shelljs/shelljs#testexpression) · [touch](https://github.com/shelljs/shelljs#touchoptions-file--file-) · [uniq](https://github.com/shelljs/shelljs#uniqoptions-input-output) · [which](https://github.com/shelljs/shelljs#whichcommand) · [exit](https://github.com/shelljs/shelljs#exitcode) · [error](https://github.com/shelljs/shelljs#error) · [errorCode](https://github.com/shelljs/shelljs#errorcode) ```js @@ -18,13 +18,15 @@ s.cat("./package.json").grep("version"); ``` … this library adds: - ['run()'](../interfaces/s.RunFunction.md) +- ['runA()'](../interfaces/s.RunAsyncFunction.md) - ['xargs()'](../interfaces/s.XargsFunction.md) - ['$()'](../interfaces/s.DollarFunction.md) **Changes/recommenctions:** - use [echo](s.md#echo) instead of `s.echo`, this was changed to `s.ShellString` for easy file writing without logging to console `s.echo("Data").to("file.txt")`. -- use ['run()'](../interfaces/s.RunFunction.md) instead of `s.exec`, because of options for passing arguments in secure way. +- use ['run()'](../interfaces/s.RunFunction.md)/['runA()'](../interfaces/s.RunAsyncFunction.md) instead of `s.exec`, because of options for passing arguments in secure way. - use ['$()'](../interfaces/s.DollarFunction.md) instead of `s.set()`, because `$()` allows chaining (you can also access config with [cli](cli.md)s `.is_*` keys). +- use [xdg](cli.md#xdg)`.temp` instead of `s.tempdir()` – the `cli.xdg.*` provides more paths than just temp directory. ## Table of contents @@ -38,6 +40,7 @@ s.cat("./package.json").grep("version"); - [XargsFunction](../interfaces/s.XargsFunction.md) - [DollarFunction](../interfaces/s.DollarFunction.md) - [RunFunction](../interfaces/s.RunFunction.md) +- [RunAsyncFunction](../interfaces/s.RunAsyncFunction.md) - [ShellReturnValue](../interfaces/s.ShellReturnValue.md) - [ListFunction](../interfaces/s.ListFunction.md) - [FindFunction](../interfaces/s.FindFunction.md) @@ -72,6 +75,7 @@ s.cat("./package.json").grep("version"); - [$](s.md#$) - [run](s.md#run) +- [runA](s.md#runa) - [cd](s.md#cd) - [pwd](s.md#pwd) - [test](s.md#test) @@ -106,13 +110,18 @@ s.cat("./package.json").grep("version"); ### Type Aliases - [RunOptions](s.md#runoptions) -- [AsyncCommandString](s.md#asynccommandstring) +- [IO](s.md#io) - [TestOptions](s.md#testoptions) - [ExecCallback](s.md#execcallback) - [ShellString](s.md#shellstring-1) - [ShellArray](s.md#shellarray) - [TouchOptionsLiteral](s.md#touchoptionsliteral) +### Classes + +- [ProcessOutput](../classes/s.ProcessOutput.md) +- [ProcessPromise](../classes/s.ProcessPromise.md) + ### Variables - [env](s.md#env) @@ -155,7 +164,7 @@ s.$("-g").rm("*.tx"); //remove only "*.txt" file #### Defined in -[src/shelljs.d.ts:54](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L54) +[src/shelljs.d.ts:54](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L54) ▸ **$**(): [`ShellString`](s.md#shellstring) @@ -165,7 +174,7 @@ s.$("-g").rm("*.tx"); //remove only "*.txt" file #### Defined in -[src/shelljs.d.ts:55](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L55) +[src/shelljs.d.ts:55](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L55) ___ @@ -173,7 +182,8 @@ ___ ▸ **run**(`command`, `vars?`): [`ShellString`](s.md#shellstring) -Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). +Executes the given command synchronously, because of that it does not know whether it will be piped, +so by default prints the command output. You can off that by prepend `….$().run`. #### Parameters @@ -186,16 +196,16 @@ Executes the given command. You can use `&` in `command` to run command asynchro [`ShellString`](s.md#shellstring) -Returns an object containing the return code and output as string, - or if `{async: true}` or a `callback` was passed, a `ChildProcess`. +Returns [ShellString](s.md#shellstring). #### Defined in -[src/shelljs.d.ts:94](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L94) +[src/shelljs.d.ts:87](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L87) ▸ **run**(`command`, `vars`, `options`): [`ShellString`](s.md#shellstring) -Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). +Executes the given command synchronously, because of that it does not know whether it will be piped, +so by default prints the command output. You can off that by prepend `….$().run`. #### Parameters @@ -203,68 +213,74 @@ Executes the given command. You can use `&` in `command` to run command asynchro | :------ | :------ | :------ | | `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | | `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | [`ExecOptions`](../interfaces/s.ExecOptions.md) & { `async?`: `boolean` \| ``"child"`` ; `needle?`: `RegExp` } & { `async?`: ``false`` } | Silence and synchronous options. | +| `options` | [`RunOptions`](s.md#runoptions) | Silence and synchronous options. | #### Returns [`ShellString`](s.md#shellstring) -Returns an object containing the return code and output as string, - or if `{async: true}` or a `callback` was passed, a `ChildProcess`. +Returns [ShellString](s.md#shellstring). #### Defined in -[src/shelljs.d.ts:111](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L111) - -▸ **run**(`command`, `vars`, `options`): `Promise`<`string`\> +[src/shelljs.d.ts:104](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L104) -Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | -| `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | [`ExecOptions`](../interfaces/s.ExecOptions.md) & { `async?`: `boolean` \| ``"child"`` ; `needle?`: `RegExp` } & { `async`: ``true`` } | Silence and synchronous options. | - -#### Returns +___ -`Promise`<`string`\> +### runA -Returns an object containing the return code and output as string, - or if `{async: true}` or a `callback` was passed, a `ChildProcess`. +▸ **runA**(`command`, `vars`): [`ProcessPromise`](../classes/s.ProcessPromise.md) -#### Defined in +Executes the given command asynchronously. +```js +s.$().runA("git branch --show-current") +.pipe(echo.bind(echo, "success:")) +.catch(echo.bind(echo, "error:")) -[src/shelljs.d.ts:127](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L127) +const ch= s.$().runA("git branch --show-current"); +ch.child.on("data", echo); -▸ **run**(`command`, `vars?`, `options?`): `Promise`<`string`\> +const result_a= await s.$().runA("git branch --show-current"); +echo(result_a.toString()); -Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). +const result_b= await s.$().runA("git branch --show-::var::", { var: "current" }, { silent: true }); +echo(result_b.toString()); +``` #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `command` | \`${string} &\` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | -| `vars?` | ``false`` \| {} | Arguments for `command`. | -| `options?` | [`RunOptions`](s.md#runoptions) | Silence and synchronous options. | +| `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | +| `vars` | ``false`` \| {} | Arguments for `command`. | #### Returns -`Promise`<`string`\> +[`ProcessPromise`](../classes/s.ProcessPromise.md) -Returns an object containing the return code and output as string, - or if `{async: true}` or a `callback` was passed, a `ChildProcess`. +Returns [ProcessPromise](../classes/s.ProcessPromise.md). #### Defined in -[src/shelljs.d.ts:143](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L143) +[src/shelljs.d.ts:152](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L152) -▸ **run**(`command`, `vars`, `options`): [`ChildProcess`](../classes/s.child.ChildProcess.md) +▸ **runA**(`command`, `vars`, `options`): [`ProcessPromise`](../classes/s.ProcessPromise.md) -Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). +Executes the given command asynchronously. +```js +s.$().runA("git branch --show-current") +.pipe(echo.bind(echo, "success:")) +.catch(echo.bind(echo, "error:")) + +const ch= s.$().runA("git branch --show-current"); +ch.child.on("data", echo); + +const result_a= await s.$().runA("git branch --show-current"); +echo(result_a.toString()); + +const result_b= await s.$().runA("git branch --show-::var::", { var: "current" }, { silent: true }); +echo(result_b.toString()); +``` #### Parameters @@ -272,18 +288,17 @@ Executes the given command. You can use `&` in `command` to run command asynchro | :------ | :------ | :------ | | `command` | `string` | String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. | | `vars` | ``false`` \| {} | Arguments for `command`. | -| `options` | `never` | Silence and synchronous options. | +| `options` | [`RunOptions`](s.md#runoptions) | Silence and synchronous options. | #### Returns -[`ChildProcess`](../classes/s.child.ChildProcess.md) +[`ProcessPromise`](../classes/s.ProcessPromise.md) -Returns an object containing the return code and output as string, - or if `{async: true}` or a `callback` was passed, a `ChildProcess`. +Returns [ProcessPromise](../classes/s.ProcessPromise.md). #### Defined in -[src/shelljs.d.ts:158](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L158) +[src/shelljs.d.ts:165](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L165) ___ @@ -2205,21 +2220,21 @@ node_modules/@types/shelljs/index.d.ts:1164 ### RunOptions -Ƭ **RunOptions**: [`ExecOptions`](../interfaces/s.ExecOptions.md) & { `async?`: ``"child"`` \| `boolean` ; `needle?`: `RegExp` } +Ƭ **RunOptions**: [`ExecOptions`](../interfaces/s.ExecOptions.md) & { `needle?`: `RegExp` } #### Defined in -[src/shelljs.d.ts:59](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L59) +[src/shelljs.d.ts:59](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L59) ___ -### AsyncCommandString +### IO -Ƭ **AsyncCommandString**: \`${string} &\` +Ƭ **IO**: [`StdioPipe`](s.child.md#stdiopipe) \| [`StdioNull`](s.child.md#stdionull) #### Defined in -[src/shelljs.d.ts:74](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/shelljs.d.ts#L74) +[src/shelljs.d.ts:109](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/shelljs.d.ts#L109) ___ diff --git a/docs/modules/xdg_.md b/docs/modules/xdg_.md index 852c83f..2403a9d 100644 --- a/docs/modules/xdg_.md +++ b/docs/modules/xdg_.md @@ -23,7 +23,7 @@ #### Defined in -[src/xdg.d.ts:1](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L1) +[src/xdg.d.ts:1](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L1) ___ @@ -55,7 +55,7 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) ___ @@ -73,7 +73,7 @@ ___ #### Defined in -[src/xdg.d.ts:12](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L12) +[src/xdg.d.ts:12](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L12) ___ @@ -83,4 +83,4 @@ ___ #### Defined in -[src/xdg.d.ts:13](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L13) +[src/xdg.d.ts:13](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L13) diff --git a/docs/modules/xdg_.xdg.md b/docs/modules/xdg_.xdg.md index c1b8d7c..00aea44 100644 --- a/docs/modules/xdg_.xdg.md +++ b/docs/modules/xdg_.xdg.md @@ -43,7 +43,7 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) ___ @@ -71,7 +71,7 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) ___ @@ -99,7 +99,7 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) ___ @@ -127,7 +127,7 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) ___ @@ -155,4 +155,4 @@ cli.xdg.home("Documents") //=> (on my linux) /home/jaandrle/Documents #### Defined in -[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/9ae5d73/src/xdg.d.ts#L11) +[src/xdg.d.ts:11](https://github.com/jaandrle/nodejsscript/blob/6b875ec/src/xdg.d.ts#L11) diff --git a/examples/background-process.mjs b/examples/background-process.mjs index 10a9100..ae190ac 100755 --- a/examples/background-process.mjs +++ b/examples/background-process.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env nodejsscript /* jshint esversion: 8,-W097, -W040, node: true, expr: true, undef: true *//* global echo, exit, s, fetch */ -const serve= s.run("npx serve", false, { async: "child" }); +const serve= s.runA("npx serve"); for await (const chunk of serve.stdout) if (chunk.includes('Accepting connections')) break; diff --git a/examples/cli.mjs b/examples/cli.mjs index b15b8a5..e0b0ce8 100755 --- a/examples/cli.mjs +++ b/examples/cli.mjs @@ -1,13 +1,11 @@ #!/usr/bin/env nodejsscript /* jshint esversion: 8,-W097, -W040, node: true, expr: true, undef: true *//* global echo, cli, pipe, s */ -import { join } from "node:path"; - cli.api("", true) .version("0.1.0") .describe("NodeJS Script cli test") .option("--clear", "Clears cerated temp dir") .action(function main({ clear }){ - const name= join(s.tempdir(), "foo bar"); + const name= cli.xdg.temp`foo bar`; s.mkdir("-p", name); const testDir= pipe(s.test.bind(null, "-d"), echo); diff --git a/examples/runA.mjs b/examples/runA.mjs new file mode 100755 index 0000000..d4599e1 --- /dev/null +++ b/examples/runA.mjs @@ -0,0 +1,10 @@ +#!/usr/bin/env nodejsscript +/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: false *//* global echo, s */ +(async function main(){ + await s.runA`git branch --show-current`.pipe(s.runA`head`); + await s.runA("git branch --show-:0:", [ "current" ], { needle: /:(\d)+:/g }); + echo(await s.cat("package.json").runA("tail").pipe(s=> s.grep("type"))); + await s.$("-F").runA("npm list").pipe(s=> s.grep("type").xargs(echo)).catch(echo.bind(null, "E")); + await s.$("-F").runA("npM list").pipe(s=> s.grep("type").xargs(echo)).catch(echo.bind(null, "E")); + s.runA("npm list").then(console.log); +})(); diff --git a/package.json b/package.json index 999b3ac..19e5c8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodejsscript", - "version": "0.7.1", + "version": "0.8.0", "author": "Jan Andrle ", "license": "MIT", "description": "A tool for writing better scripts", diff --git a/src/cli.d.ts b/src/cli.d.ts index 3342f2b..623ba7e 100644 --- a/src/cli.d.ts +++ b/src/cli.d.ts @@ -137,4 +137,13 @@ export namespace cli{ * @category Public */ const xdg: typeof xdg_.xdg; + + /** + * Throws user targeted error + * ```js + * const number= await cli.read({ "-p". "Insert a number:" }); + * if(Number.isNaN(Number(number))) cli.error(`Provided text '${number}' is not a number`); + * ``` + * */ + function error(message: string): Error; } diff --git a/src/runA-utils.js b/src/runA-utils.js new file mode 100644 index 0000000..a9121c4 --- /dev/null +++ b/src/runA-utils.js @@ -0,0 +1,214 @@ +import chalk from "ansi-colors"; +import { spawn } from "node:child_process"; +import assert from "node:assert"; +import { inspect } from 'node:util'; +import { AsyncLocalStorage, createHook } from 'node:async_hooks'; +import shelljs from "shelljs"; +const { which, ShellString }= shelljs; + +const exit_codes= { 2: 'Misuse of shell builtins', 126: 'Invoked command cannot execute', 127: 'Command not found', 128: 'Invalid exit argument', 129: 'Hangup', 130: 'Interrupt', 131: 'Quit and dump core', 132: 'Illegal instruction', 133: 'Trace/breakpoint trap', 134: 'Process aborted', 135: 'Bus error: "access to undefined portion of memory object"', 136: 'Floating point exception: "erroneous arithmetic operation"', 137: 'Kill (terminate immediately)', 138: 'User-defined 1', 139: 'Segmentation violation', 140: 'User-defined 2', 141: 'Write to pipe with no one reading', 142: 'Signal raised by alarm', 143: 'Termination (request to terminate)', 145: 'Child process terminated, stopped (or continued*)', 146: 'Continue if stopped', 147: 'Stop executing temporarily', 148: 'Terminal stop signal', 149: 'Background process attempting to read from tty ("in")', 150: 'Background process attempting to write to tty ("out")', 151: 'Urgent data available on socket', 152: 'CPU time limit exceeded', 153: 'File size limit exceeded', 154: 'Signal raised by timer counting virtual time: "virtual timer expired"', 155: 'Profiling timer expired', 157: 'Pollable event', 159: 'Bad syscall', }; +const processCwd= Symbol('processCwd'); +const storage= new AsyncLocalStorage(); +createHook({ + init: syncCwd, + before: syncCwd, + promiseResolve: syncCwd, + after: syncCwd, + destroy: syncCwd, +}).enable(); +const defaults= getDefaults(); +function getStore(){ return storage.getStore() || defaults; } +export const process_store= getProcesStore(()=> ({ + command: '', from: '', + resolve: noop, reject: noop, + stdio: ['inherit', 'pipe', 'pipe'], + resolved: false, piped: 0, + prerun: noop, postrun: noop +})); +export class ProcessOutput extends Error { + constructor({ message, code, ...rest }){ + super(message); + for(const [ k, value ] of Object.entries(rest)) + Reflect.defineProperty(this, k, { value, writable: false }); + Reflect.defineProperty(this, "exitCode", { value: code, writable: false }); + const combined= rest.stdout + ( rest.stderr ? "\n"+rest.stderr : ""); + Reflect.defineProperty(this, "toString", { value: ()=> combined, writable: false }); + } + [inspect.custom]() { + let stringify = (s, c) => s.length === 0 ? "''" : c(inspect(s)); + return [ + "ProcessOutput {", + ` stdout: ${stringify(this.stdout, chalk.green)},`, + ` stderr: ${stringify(this.stderr, chalk.red)},`, + ` signal: ${inspect(this.signal)},`, + " exitCode: " + (this.exitCode === 0 ? chalk.green : chalk.red)(this.exitCode) + " " + + ( exitCodeInfo(this.exitCode) ? chalk.grey(' (' + exitCodeInfo(this.exitCode) + ')') : '' ), + "}" + ].join("\n"); + } +} +export class ProcessPromise extends Promise{ + static create(command, from, options){ + let resolve, reject; + const i= new this((...args) => ([resolve, reject] = args)); + process_store.assign(i, { resolve, reject, command, from, options }); + setImmediate(()=> i._run()); // Postpone run to allow promise configuration. + return i; + } + _run(){ + if(this.child) return; // The _run() can be called from a few places. + const { prerun, command, options, stdio }= process_store.get(this); + prerun(); // In case $1.pipe($2), the $2 returned, and on $2._run() invoke $1._run(). + const { spawn, prefix, shell, [processCwd]: cwd, env }= getStore(); + if(!Reflect.has(options, "cwd")) options.cwd= cwd; + if(Reflect.has(options, "pipe")) stdio[0]= "pipe"; + this.child= spawn(prefix + command, + Object.assign({ shell: typeof shell === 'string' ? shell : true, windowsHide: true, env, stdio }, options)); + if(Reflect.has(options, "pipe")) this.child.stdin.end(options.pipe); + let stdout = '', stderr = ''; + const { from, resolve, reject }= process_store.get(this); + this.child.on('close', (code, signal)=> { + let message= `exit code: ${code}`; + if (code != 0 || signal != null) { + message = `${stderr || '\n'} at ${from}`; + message += `\n exit code: ${code}${exitCodeInfo(code) ? ' (' + exitCodeInfo(code) + ')' : ''}`; + if (signal != null) { + message += `\n signal: ${signal}`; + } + } + const output= new ProcessOutput({ code, signal, stdout, stderr, message }); + process_store.get(this).resolved= true; + if(code === 0 || !process_store.get(this).options.fatal) + return resolve(output); + reject(output); + }); + this.child.on('error', (err) => { + const message = `${err.message}\n` + + //` errno: ${err.errno} (${errnoMessage(err.errno)})\n` + + ` errno: ${err.errno}\n` + + ` code: ${err.code}\n` + + ` at ${process_store.get(this).from}`; + reject(new ProcessOutput({ stdout, stderr, message })); + process_store.get(this).resolved= true; + }); + const { piped, postrun, options: { silent } }= process_store.get(this); + const log= piped || silent ? ( ()=> ({}) ) : d=> console.log(d.toString()); + if(piped<2) this.child.stdout?.on('data', d=> ( stdout+= d, log(d) )); + this.child.stderr?.on('data', d=> ( stderr+= d, log(d) )); + postrun(); // In case $1.pipe($2), after both subprocesses are running, we can pipe $1.stdout to $2.stdin. + if(this._timeout && this._timeoutSignal) { + const t = setTimeout(() => this.kill(this._timeoutSignal), this._timeout); + this.finally(() => clearTimeout(t)).catch(noop); + } + } + pipe(dest) { + if(process_store.get(this).resolved) { + if(dest instanceof ProcessPromise) dest.stdin.end(); // In case of piped stdin, we may want to close stdin of dest as well. + throw new Error("The pipe() method shouldn't be called after promise is already resolved!"); + } + process_store.get(this).piped= 1; + if(typeof dest === "function") + return this.then(({ stdout, stderr, exitCode })=> dest(ShellString(stdout, stderr, exitCode))); + + process_store.get(this).piped= 2; + if(dest instanceof ProcessPromise){ + dest.stdio('pipe'); + process_store.assign(dest, { + prerun: this._run.bind(this), + postrun: ()=> { + if (!dest.child) throw new Error('Access to stdin of pipe destination without creation a subprocess.'); + this.stdout.pipe(dest.stdin); + }}); + return dest; + } + process_store.get(this).postrun = () => this.stdout.pipe(dest); + return this; + } + //async kill(signal = 'SIGTERM') { + // if (!this.child) throw new Error('Trying to kill a process without creating one.'); + // if (!this.child.pid) throw new Error('The process pid is undefined.'); + //=> let children= await psTree(this.child.pid); + // for (const p of children) { + // try { process.kill(+p.PID, signal); } + // catch (e) { } + // } + // try { process.kill(this.child.pid, signal); } + // catch (e) { } + //} + stdio(stdin, stdout = 'pipe', stderr = 'pipe') { + process_store.get(this).stdio= [stdin, stdout, stderr]; + return this; + } + get stdin(){ + process_store.get(this).stdio= 'pipe'; + return stdInOutErr(this, "stdin"); } + get stdout(){ + return stdInOutErr(this, "stdout"); } + get stderr(){ + return stdInOutErr(this, "stderr"); } + timeout(d, signal = 'SIGTERM') { + this._timeout = parseDuration(d); + this._timeoutSignal = signal; + return this; + } +} +function stdInOutErr(target, name){ + target._run(); + assert(target.child); + if (target.child[name] == null) + throw new Error(`The ${name} of subprocess is null.`); + return target.child[name]; +} +function noop(){} +function getProcesStore(initial){ + const store= new WeakMap(); + return { + has: store.has.bind(store), + set: store.set.bind(store), + get(target){ return store.has(target) ? store.get(target) : initial; }, + assign(target, data){ + const now= store.has(target) ? store.get(target) : initial(); + store.set(target, Object.assign(now, data)); + return now; + } + }; +}function syncCwd() { + const cwd= getStore()[processCwd]; + if(cwd != process.cwd()) + process.chdir(cwd); +} +function exitCodeInfo(exitCode) { return exit_codes[exitCode || -1]; } +export function parseDuration(d) { + if (typeof d == 'number') { + if (isNaN(d) || d < 0) + throw new Error(`Invalid duration: "${d}".`); + return d; + } + else if (/\d+s/.test(d)) { + return +d.slice(0, -1) * 1000; + } + else if (/\d+ms/.test(d)) { + return +d.slice(0, -2); + } + throw new Error(`Unknown duration: "${d}".`); +} +function getDefaults(){ + const defaults= { + [processCwd]: process.cwd(), + verbose: true, + env: process.env, + shell: true, + prefix: '', + spawn + }; + try { + if (process.platform !== 'win32') { + defaults.shell= which("bash").stdout; + defaults.prefix= 'set -euo pipefail;'; + } + } + catch (err) { + // ¯\_(ツ)_/¯ + } + return defaults; +} diff --git a/src/shelljs.d.ts b/src/shelljs.d.ts index 8ab6004..d641c5c 100644 --- a/src/shelljs.d.ts +++ b/src/shelljs.d.ts @@ -57,13 +57,6 @@ export interface DollarFunction{ export const $: DollarFunction; export type RunOptions= ExecOptions & { - /** - * Asynchronous execution. - * - * - * @default false - */ - async?: "child" | boolean | undefined; /** * Pattern in `command` to be replacced by variables. * @@ -71,10 +64,10 @@ export type RunOptions= ExecOptions & { */ needle?: RegExp; } -export type AsyncCommandString= `${string} &`; export interface RunFunction { /** - * Executes the given command synchronously. + * Executes the given command synchronously, because of that it does not know whether it will be piped, + * so by default prints the command output. You can off that by prepend `….$().run`. * * *Synchronous simple examples*: * ```js @@ -89,12 +82,13 @@ export interface RunFunction { * * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. * @param vars Arguments for `command`. - * @return Returns an object containing the return code and output as string. + * @return Returns an object containing the return code and output as {@link ShellString}. */ (command: string, vars?: {}): ShellString; /** - * Executes the given command synchronously. + * Executes the given command synchronously, because of that it does not know whether it will be piped, + * so by default prints the command output. You can off that by prepend `….$().run`. * * *Passing variables*: * ```js @@ -104,72 +98,109 @@ export interface RunFunction { * * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. * @param vars Arguments for `command`. - * @param options Silence and synchronous options. - * @return Returns an object containing the return code and output as string, - * or if `{async: true}` was passed, a `ChildProcess`. + * @param options Silence and options. + * @return Returns an object containing the return code and output as {@link ShellString}. */ - (command: string, vars: {} | false, options: RunOptions & { async?: false | undefined }): ShellString; - + (command: string, vars: {} | false, options: RunOptions): ShellString; +} +import { Readable, Writable } from 'node:stream'; +import { inspect } from 'node:util'; +import { ChildProcess, StdioNull, StdioPipe } from 'node:child_process'; +export declare type IO= StdioPipe | StdioNull; +export declare class ProcessOutput extends Error { + constructor(code: number | null, signal: NodeJS.Signals | null, stdout: string, stderr: string, combined: string, message: string); + toString(): string; + get stdout(): string; + get stderr(): string; + get exitCode(): number | null; + get signal(): NodeJS.Signals | null; + [inspect.custom](): string; +} +export declare class ProcessPromise extends Promise { + child?: ChildProcess; + _run(): void; + get stdin(): Writable; + get stdout(): Readable; + get stderr(): Readable; + stdio(stdin: IO, stdout?: IO, stderr?: IO): this; + pipe(dest: Writable | ProcessPromise | ((s: ShellString)=> any)): ProcessPromise; + // kill(signal?: string): Promise; + // timeout(d: Duration, signal?: string): this; +} +export interface RunAsyncFunction { /** * Executes the given command asynchronously. * ```js - * s.$().run("git branch --show-current", false, { async: true }) + * s.$().runA("git branch --show-current") * .then(echo.bind(echo, "success:")) - * .catch(echo.bind(echo, "error:")) - * ``` + * .catch(echo.bind(echo, "error:")); * - * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. - * @param vars Arguments for `command`. - * @param options Silence and synchronous options. - * @return Returns an object containing the return code and output as string, - * or if `{async: true}` was passed, a `Promise`. - */ - (command: string, vars: {} | false, options: RunOptions & { async: true }): Promise; - - /** - * Executes the given command asynchronously. - * ```js - * s.$().run("git branch --show-current", false, { async: true }) - * .then(echo.bind(echo, "success:")) - * .catch(echo.bind(echo, "error:")) + * s.$().runA("npm list") + * .pipe(s=> echo(s.grep("types"))) + * .catch(echo.bind(echo, "error:")); + * + * const ch= s.$().runA("git branch --show-current"); + * ch.child.on("data", echo); + * + * const result_a= await s.$().runA("git branch --show-current"); + * echo(result_a.toString()); * ``` * * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. * @param vars Arguments for `command`. - * @param options Silence and synchronous options. - * @return Returns an object containing the return code and output as string, - * or if `{async: true}` was passed, a `Promise`. */ - (command: AsyncCommandString, vars?: {} | false, options?: RunOptions): Promise; + (command: string, vars: {} | false): ProcessPromise; /** - * Executes the given command asynchronously. *Get the {@link child}*: + * Executes the given command asynchronously. * ```js - * const ch= s.$().run("git branch --show-current", false, { async: "child" }); - * ch.on("data", echo); + * const result_b= await s.$().runA("git branch --show-::var::", { var: "current" }, { silent: true }); + * echo(result_b.toString()); * ``` * * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. * @param vars Arguments for `command`. - * @param options Silence and synchronous options. - * @return Returns an object containing the return code and output as string, - * or if `{async: "child"}` was passed, a `ChildProcess`. + * @param options Silence and options. */ - (command: string, vars: {} | false, options: RunOptions & { async: "child" }): child.ChildProcess; + (command: string, vars: {} | false, options: RunOptions): ProcessPromise; } /** - * Executes the given command. You can use `&` in `command` to run command asynchronously (but `options.async` has higher priority). + * Executes the given command synchronously, because of that it does not know whether it will be piped, + * so by default prints the command output. You can off that by prepend `….$().run`. * * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. * @param vars Arguments for `command`. * @param options Silence and synchronous options. - * @return Returns an object containing the return code and output as string, - * or if `{async: true}` or a `callback` was passed, a `ChildProcess`. + * @return Returns {@link ShellString}. */ export const run: RunFunction; +/** + * Executes the given command asynchronously. + * ```js + * s.$().runA("git branch --show-current") + * .pipe(echo.bind(echo, "success:")) + * .catch(echo.bind(echo, "error:")) + * + * const ch= s.$().runA("git branch --show-current"); + * ch.child.on("data", echo); + * + * const result_a= await s.$().runA("git branch --show-current"); + * echo(result_a.toString()); + * + * const result_b= await s.$().runA("git branch --show-::var::", { var: "current" }, { silent: true }); + * echo(result_b.toString()); + * ``` + * + * @param command String of command(s) to be executed. Defined patterns (by default `/::([^:]+)::/g`) will be replaced by actual value. + * @param vars Arguments for `command`. + * @param options Silence and synchronous options. + * @return Returns {@link ProcessPromise}. + */ +export const runA: RunAsyncFunction; export interface ShellReturnValue{ xargs: XargsFunction $: DollarFunction, run: RunFunction + runA: RunAsyncFunction } diff --git a/src/shelljs.js b/src/shelljs.js index 0321d27..4d0ea22 100644 --- a/src/shelljs.js +++ b/src/shelljs.js @@ -1,27 +1,20 @@ import shelljs from "shelljs"; import plugin from "shelljs/plugin.js"; import escape from "shell-escape-tag"; +import { ProcessPromise, ProcessOutput } from "./runA-utils.js"; +export { ProcessPromise, ProcessOutput }; shelljs.echo= shelljs.ShellString; -plugin.register("xargs", xargs, { - canReceivePipe: true, - wrapOutput: false, +plugin.register("xargs", xargs, { canReceivePipe: true, wrapOutput: false, cmdOptions: { I: 'needle', R: 'is_raw' } }); -plugin.register("$", $, { - canReceivePipe: true, - wrapOutput: false -}); -plugin.register('run', run, { - unix: false, - canReceivePipe: true, - wrapOutput: false -}); - +plugin.register("$", $, { canReceivePipe: true, wrapOutput: false }); +plugin.register('run', run, { unix: false, canReceivePipe: true, wrapOutput: false }); +plugin.register('runA', runA, { unix: false, canReceivePipe: true, wrapOutput: false }); export default shelljs; function xargs({ needle, is_raw }, ...args){ @@ -60,33 +53,41 @@ function $(config_next){ } }); } +function runArgumentsToCommand(from, pieces, args){ + if(typeof pieces === "string"){ + const [ vars= {}, options= {} ]= args; + const { needle= /::([^:]+)::/g }= options; + Reflect.deleteProperty(options, "needle"); + if(Object.keys(vars).length) + return [ pieces.replace(needle, function replace(_, key){ + return escape([ "" ], [ vars[key] ]); + }), options ]; + else + return [ pieces ]; + } else if(pieces.some((p)=> p == undefined)) { + throw new Error(`Malformed command at ${from}`); + } else { + return [ escape(pieces, args) ]; + } +} /** @this {shelljs} */ -function run(command, vars, options){ +function run(pieces, ...args){ /* jshint ignore:start */ const s= this || shelljs; /* jshint ignore:end *//* global s */ - vars= vars || {}; - options= options || {}; - command= command.replace(options.needle || /::([^:]+)::/g, function replace(_, key){ - return escape([ "" ], [ vars[key] ]); - }); - if(command[command.length-1]==="&"){ - if(!Reflect.has(options, "async")) - Reflect.set(options, "async", true); - command= command.slice(0, command.length-1); - } - Reflect.deleteProperty(options, "needle"); - if(options.async !== true){ - if(typeof options.async === "string") options.async= true; + const from= new Error().stack.split(/^\s*at\s/m).find(l=> l.indexOf("async main")===0 || l.indexOf("main")===0).trim(); + const [ command, options= {} ]= runArgumentsToCommand(from, pieces, args); + try { return s.exec(command, options); + } catch(e){ + const err= new Error(e.message.split("\n").slice(0, 1).join("\n")+"\n at "+from); + throw err; } - return new Promise(function(resolve, reject){ - const callback= function(code, stdout, stderr){ - if(!code) return resolve(stdout); - const e= new Error(stderr); - e.code= code; - reject(e); - }; - return s.exec(command, options, callback); - }); +} +function runA(pieces, ...args){ + const from= new Error().stack.split(/^\s*at\s/m).find(l=> l.indexOf("async main")===0 || l.indexOf("main")===0).trim(); + const [ command, options= {} ]= runArgumentsToCommand(from, pieces, args); + const pipe= plugin.readFromPipe(); + if(pipe) options.pipe= pipe; + return ProcessPromise.create(command, from, Object.assign({}, shelljs.config, options)); }