{"id":16681450,"url":"https://github.com/electrode-io/xarc-run","last_synced_at":"2025-03-17T00:32:37.534Z","repository":{"id":65910281,"uuid":"91870251","full_name":"electrode-io/xarc-run","owner":"electrode-io","description":"npm run scripts concurrently and serially, and more.","archived":false,"fork":false,"pushed_at":"2021-10-28T05:50:41.000Z","size":347,"stargazers_count":43,"open_issues_count":3,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-09T13:35:21.127Z","etag":null,"topics":["build","concurrent","executor","javascript","namespace","nodejs","npm","runner","scripts","serial","task","tasks","tool"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/electrode-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-20T06:34:43.000Z","updated_at":"2025-02-09T14:14:26.000Z","dependencies_parsed_at":"2023-02-15T19:40:16.752Z","dependency_job_id":null,"html_url":"https://github.com/electrode-io/xarc-run","commit_stats":null,"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electrode-io%2Fxarc-run","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electrode-io%2Fxarc-run/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electrode-io%2Fxarc-run/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electrode-io%2Fxarc-run/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/electrode-io","download_url":"https://codeload.github.com/electrode-io/xarc-run/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243835942,"owners_count":20355611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build","concurrent","executor","javascript","namespace","nodejs","npm","runner","scripts","serial","task","tasks","tool"],"created_at":"2024-10-12T14:04:15.360Z","updated_at":"2025-03-17T00:32:37.233Z","avatar_url":"https://github.com/electrode-io.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM version][npm-image]][npm-url] [![Build Status][ci-shield]][ci-url]\n[![Dependency Status][daviddm-image]][daviddm-url] [![devDependency Status][daviddm-dev-image]][daviddm-dev-url]\n[![coverage][coverage-image]][coverage-url]\n\n# @xarc/run\n\n`npm run` enhanced.\n\n- Compatible with `npm run` for [npm scripts]\n- Run them concurrently or serially\n- Extend them with JavaScript\n- Group them with namespace\n- and [more](#full-list-of-features)\n\n## Running [npm scripts]\n\nThis module provides a command `xrun` to run all your [npm scripts] in `package.json`.\n\nAnd you can run multiple of them **concurrently** or **serially**.\n\nSome examples below:\n\n| what you want to do                 | npm command    | `xrun` command            |\n| ----------------------------------- | -------------- | ------------------------- |\n| run `test`                          | `npm run test` | `xrun test`               |\n| run `lint` and `test` concurrently  | N/A            | `xrun lint test`          |\n| run `lint` and then `test` serially | N/A            | `xrun --serial lint test` |\n\nAlias for the options:\n\n- `-s`: `--serial`\n\n## Running JavaScript tasks\n\nYou can write your tasks in JavaScript and run them with `xrun`.\n\n\u003e This is useful when a shell script is too long to fit in a JSON string, or when it's not easy to do something with shell script.\n\nThese APIs are provided: `concurrent`, `serial`, `exec`, `env`, and `load`.\n\nPut your tasks in a file `xrun-tasks.js` and `xrun` will load it automatically.\n\nAn example `xrun-tasks.js`:\n\n```js\nconst { load, exec, concurrent, serial } = require(\"@xarc/run\");\nload({\n  //\n  // define a task hello, with a string definition\n  // because a string is the task's direct value, it will be executed as a shell command.\n  //\n  hello: \"echo hello\",\n  //\n  // define a task world, using a JavaScript function to print something\n  //\n  world: () =\u003e console.log(\"world\"),\n  //\n  // define a task serialTask, that will execute the three tasks serially, first two are\n  // the hello and world tasks defined above, and 3rd one is a shell command defined with exec.\n  // because the 3rd one is not a direct value of a task, it has to use exec to define a shell command.\n  //\n  serialTask: serial(\"hello\", \"world\", exec(\"echo hi from exec\")),\n  //\n  // define a task concurrentTask, that will execute the three tasks concurrently\n  //\n  concurrentTask: concurrent(\"hello\", \"world\", exec(\"echo hi from exec\")),\n  //\n  // define a task nesting, that does complex nesting of concurrent/serial constructs\n  //\n  nesting: concurrent(serial(\"hello\", \"world\"), serial(\"serialTask\", concurrent(\"hello\", \"world\")))\n});\n```\n\nTo run the tasks defined above from the command prompt, below are some examples:\n\n| what you want to do                   | command                     |\n| ------------------------------------- | --------------------------- |\n| run `hello`                           | `xrun hello`                |\n| run `hello` and `world` concurrently  | `xrun hello world`          |\n| run `hello` and then `world` serially | `xrun --serial hello world` |\n\n### `exec` and shell scripts\n\nUse `exec` to invoke a shell command from JavaScript.\n\nHere are some examples:\n\n| shell script in JSON string | shell script using `exec` in JavaScript          | note                         |\n| --------------------------- | ------------------------------------------------ | ---------------------------- |\n| `echo hello`                | `exec(\"echo hello\")`                             |                              |\n| `FOO=bar echo hello $FOO`   | `exec(\"FOO=bar echo hello $FOO\")`                |                              |\n| `echo hello \u0026\u0026 echo world`  | `exec(\"echo hello \u0026\u0026 echo world\")`               |                              |\n| `echo hello \u0026\u0026 echo world`  | `serial(exec(\"echo hello\"), exec(\"echo world\"))` | using serial instead of `\u0026\u0026` |\n\n- `exec` supports `options` that can set a few things. Some examples below:\n\n| what you want to do                   | shell script using `exec` in JavaScript                            |\n| ------------------------------------- | ------------------------------------------------------------------ |\n| setting an env variable               | `exec(\"echo hello $FOO\", {env: {FOO: \"bar\"}})`                     |\n| provide tty to the shell process      | `exec(\"echo hello\", {flags: \"tty\"})`                               |\n| using spawn with tty, and setting env | `exec(\"echo hello $FOO\", {flags: \"tty,spawn\", env: {FOO: \"bar\"}})` |\n\n### Function tasks\n\nA task in JavaScript can be just a function.\n\n```js\nload({\n  hello: () =\u003e console.log(\"hello\")\n});\n```\n\nA function task can do a few things:\n\n- Return a promise or be an async function, and `xrun` will wait for the Promise.\n- Return a stream and `xrun` will wait for the stream to end.\n- Return another task for `xrun` to execute further.\n- Access arguments with `context.argOpts`.\n\nExample:\n\n```js\nload({\n  // A function task named hello that access arguments with `context.argOpts`\n  async hello(context) {\n    console.log(\"hello argOpts:\", context.argOpts);\n    return [\"foo\"];\n  },\n  h2: [\"hello world\"],\n  foo: \"echo bar\"\n});\n```\n\n### Running tasks with `concurrent` and `serial`\n\nUse `concurrent` and `serial` to define a task that run multiple other tasks **concurrently** or **serially**.\n\nSome examples:\n\n- To do the same thing as the shell script `echo hello \u0026\u0026 echo world`:\n\n```js\nserial(exec(\"echo hello\"), exec(\"echo world\"));\n```\n\n- or concurrently:\n\n```js\nconcurrent(exec(\"echo hello\"), exec(\"echo world\"));\n```\n\n- You can specify any valid tasks:\n\n```js\nserial(\n  exec(\"echo hello\"),\n  () =\u003e console.log(\"world\"),\n  \"name-of-a-task\",\n  concurrent(\"task1\", \"task2\")\n);\n```\n\n### Tasks to set `process.env`\n\n`env` allows you to create a task to set variables in `process.env`.\n\nYou use it by passing an object of env vars, like `env({VAR_NAME: \"var-value\"})`\n\nExamples:\n\n```js\nload({\n  setEnv: serial(env({ FOO: \"bar\" }), () =\u003e console.log(process.env.FOO))\n});\n```\n\n### And to put it all together\n\nA popular CI/CD use case is to start servers and then run tests, which can be achieved using `xrun` JavaScript tasks:\n\n```js\nconst { concurrent, serial, load, stop } = require(\"@xarc/run\");\nconst waitOn = require(\"wait-on\");\n\nconst waitUrl = url =\u003e waitOn({ resources: [url] });\n\nload({\n  \"start-server-and-test\": concurrent(\n    // start the servers concurrently\n    concurrent(\"start-mock-server\", \"start-app-server\"),\n    serial(\n      // wait for servers concurrently, and then run tests\n      concurrent(\"wait-mock-server\", \"wait-app-server\"),\n      \"run-tests\",\n      // Finally stop servers and exit.\n      // This is only needed because there are long running servers.\n      () =\u003e stop()\n    )\n  ),\n  \"start-mock-server\": \"mock-server\",\n  \"start-app-server\": \"node lib/server\",\n  \"wait-mock-server\": () =\u003e waitUrl(\"http://localhost:8000\"),\n  \"wait-app-server\": () =\u003e waitUrl(\"http://localhost:3000\"),\n  \"run-tests\": \"cypress run --headless -b chrome\"\n});\n```\n\n\u003e `xrun` adds `node_modules/.bin` to PATH. That's why `npx` is not needed to run commands like `cypress` that's installed in `node_modules`.\n\n### Shorthands\n\nNot a fan of full API names like `concurrent`, `serial`, `exec`? You can skip them.\n\n- `concurrent`: Any array of tasks are concurrent, except when they are specified at the top level.\n- `exec`: Any string starting with `~$` are treated as shell script.\n- `serial`: An array of tasks specified at the top level is executed serially.\n\nExample:\n\n```js\nload({\n  executeSerially: [\"task1\", \"task2\"], // top level array serially\n  concurrentArray: [[\"task1\", \"task2\"]], // Any other array (the one within) are concurrent\n  topLevelShell: \"echo hello\", // top level string is a shell script\n  shellScripts: [\n    \"~$echo hello\", // any string started with ~$ is shell script\n    \"~(tty,spawn)$echo hello\" // also possible to specify tty and spawn flag between ~ and $\n  ]\n});\n```\n\n## Full List of Features\n\n- **_Support [namespaces](./REFERENCE.md#namespace) for tasks._**\n- Load and execute npm scripts from `package.json`.\n- Auto completion for [bash] and [zsh].\n- Define tasks in a JavaScript file.\n- Serial tasks execution.\n- Concurrent tasks execution.\n- Proper nesting task execution hierarchy.\n- Promise, [node.js stream], or callback support for tasks written in JavaScript.\n- Run time flow control - return further tasks to execute from JS task function.\n- Support custom task execution reporter.\n- Specify complex tasks execution pattern from command line.\n- Tasks can have a [_finally_](./REFERENCE.md#finally-hook) hook that always runs after task finish or fail.\n- Support [flexible function task](./REFERENCE.md#function) that can return more tasks to run.\n\n## Getting Started\n\nStill reading? Maybe you want to take it for a test drive?\n\n## A Simple Example\n\nHere is a simple sample.\n\n1. First setup the directory and project:\n\n```bash\nmkdir xrun-test\ncd xrun-test\nnpm init --yes\nnpm install rimraf @xarc/run\n```\n\n2. Save the following code to `xrun-tasks.js`:\n\n```js\n\"use strict\";\nconst { load } = require(\"@xarc/run\");\n\nconst tasks = {\n  hello: \"echo hello world\", // a shell command to be exec'ed\n  jsFunc() {\n    console.log(\"JS hello world\");\n  },\n  both: [\"hello\", \"jsFun\"] // execute the two tasks serially\n};\n\n// Load the tasks into @xarc/run\nload(tasks);\n```\n\n3. And try one of these commands:\n\n| what to do                            | command                      |\n| ------------------------------------- | ---------------------------- |\n| run the task `hello`                  | `xrun hello`                 |\n| run the task `jsFunc`                 | `xrun jsFunc`                |\n| run the task `both`                   | `xrun both`                  |\n| run `hello` and `jsFunc` concurrently | `xrun hello jsFunc`          |\n| run `hello` and `jsFunc` serially     | `xrun --serial hello jsFunc` |\n\n## A More Complex Example\n\nHere is a more complex example to showcase a few more features:\n\n```js\n\"use strict\";\n\nconst util = require(\"util\");\nconst { exec, concurrent, serial, env, load } = require(\"@xarc/run\");\nconst rimraf = util.promisify(require(\"rimraf\"));\n\nconst tasks = {\n  hello: \"echo hello world\",\n  jsFunc() {\n    console.log(\"JS hello world\");\n  },\n  both: {\n    desc: \"invoke tasks hello and jsFunc in serial order\",\n    // only array at top level like this is default to serial, other times\n    // they are default to concurrent, or they can be marked explicitly\n    // with the serial and concurrent APIs (below).\n    task: [\"hello\", \"jsFunc\"]\n  },\n  // invoke tasks hello and jsFunc concurrently as a simple concurrent array\n  both2: concurrent(\"hello\", \"jsFunc\"),\n  shell: {\n    desc: \"Run a shell command with TTY control and set an env\",\n    task: exec({ cmd: \"echo test\", flags: \"tty\", env: { foo: \"bar\" } })\n  },\n  babel: exec(\"babel src -D lib\"),\n  // serial array of two tasks, first one to set env, second to invoke the babel task.\n  compile: serial(env({ BABEL_ENV: \"production\" }), \"babel\"),\n  // more complex nesting serial/concurrent tasks.\n  build: {\n    desc: \"Run production build\",\n    task: serial(\n      () =\u003e rimraf(\"dist\"), // cleanup, (returning a promise will be awaited)\n      env({ NODE_ENV: \"production\" }), // set env\n      concurrent(\"babel\", exec(\"webpack\")) // invoke babel task and run webpack concurrently\n    )\n  }\n};\n\nload(tasks);\n```\n\n## Global `xrun` command\n\nIf you'd like to get the command `xrun` globally, so you don't have to type `npx xrun`, you can install another small npm module [@xarc/run-cli] globally.\n\n```bash\n$ npm install -g @xarc/run-cli\n```\n\n## Load and Run Tasks Programmatically\n\nIf you don't want to use the CLI, you can load and invoke tasks in your JavaScript code using the `run` API.\n\nExample:\n\n```js\nconst { run, load, concurrent } = require(\"@xarc/run\");\nconst myTasks = require(\"./tools/tasks\");\n\nload(myTasks);\n// assume task1 and task2 are defined, below will run them concurrently\nrun(concurrent(\"task1\", \"task2\"), err =\u003e {\n  if (err) {\n    console.log(\"run tasks failed\", err);\n  } else {\n    console.log(\"tasks completed\");\n  }\n});\n```\n\n\u003e Promise version of `run` is `asyncRun`\n\n## TypeScript\n\nName your task file `xrun-tasks.ts` if you want to use TypeScript.\n\nYou also need to install [ts-node](https://www.npmjs.com/package/ts-node) to your `node_modules`\n\nie:\n\n```bash\nnpm install -D ts-node typescript\n```\n\n`xrun` automatically loads `ts-node/register` when it detects `xrun-tasks.ts` file.\n\n## Command Line Usage\n\nAny task can be invoked with the command `xrun`:\n\n```bash\n$ xrun task1 [task1 options] [\u003ctask2\u003e ... \u003ctaskN\u003e]\n```\n\nie:\n\n```bash\n$ xrun build\n```\n\nFor help on usage:\n\n```bash\n$ xrun -h\n```\n\nTo load [npm scripts] into the `npm` namespace, use the `--npm` option:\n\n```bash\n$ xrun --npm test\n```\n\nYou can also specify command line options under `@xarc/run` in your `package.json`.\n\n### Specifying Complex Tasks from command line\n\nYou can specify your tasks as an array from the command line.\n\nFor example, to have `xrun` execute the tasks `[ task_a, task_b ]` concurrently:\n\n```bash\n$ xrun [ task_a, task_b ]\n```\n\nYou can also execute them serially with:\n\n```bash\n$ xrun --serial [ task_a, task_b ]\n```\n\nYou can execute tasks serially, and then some tasks concurrently:\n\n```bash\n$ xrun --serial [task_a, task_b, [task_c1, task_c2]]\n```\n\n\u003e will execute `task_a`, then `task_b`, and finally `task_c1` and `task_c2` concurrently.\n\nYou can pass the whole array in as a single string, which will be parsed as an array with string elements only.\n\n```bash\n$ xrun \"[task_a, task_b, [task_c1, task_c2]]\"\n```\n\n## Task Name\n\nTask name is any alphanumeric string that does not contain `/`, or starts with `?` or `~$`.\n\nTasks can be invoked from command line:\n\n- `xrun foo/task1` indicates to execute `task1` in namespace `foo`\n- `xrun ?task1` or `xrun ?foo/task1` indicates that executing `task1` is optional.\n\n`xrun` treats these characters as special:\n\n- `/` as namespace separator\n- prefix `?` to let you indicate that the execution of a task is optional so it won't fail if the task is not found.\n- prefix `~$` to indicate the task to be a string as a shell command\n\n## Optional Task Execution\n\nBy prefixing the task name with `?` when invoking, you can indicate the execution of a task as optional so it won't fail in case the task is not found.\n\nFor example:\n\n- `xrun ?foo/task1` or `xrun ?task1` won't fail if `task1` is not found.\n\n## Task Definition\n\nA task can be `string`, `array`, `function`, or `object`. See [reference](./REFERENCE.md#task-definition) for details.\n\n## package.json\n\nYou can define @xarc/run tasks and options in your `package.json`.\n\n## Tasks\n\nYou can also define **xrun** tasks without JavaScript capability in your `package.json`.\n\nThey will be loaded into a namespace `pkg`.\n\nFor example:\n\n```js\n{\n  \"name\": \"my-app\",\n  \"@xarc/run\": {\n    \"tasks\": {\n      \"task1\": \"echo hello from package.json\",\n      \"task2\": \"echo hello from package.json\",\n      \"foo\": [\"task1\", \"task2\"]\n    }\n  }\n}\n```\n\nAnd you can invoke them with `xrun pkg/foo`, or `xrun foo` if there are no other namespace with a task named `foo`.\n\n## Options\n\nCommand line options can also be specified under `@xarc/run` inside your `package.json`.\n\nFor example:\n\n```js\n{\n  \"name\": \"my-app\",\n  \"@xarc/run\": {\n    \"npm\": true\n  }\n}\n```\n\n## Async Tasks\n\nYou can provide a JS function for a task that executes asynchronously. Your function just need to take a callback or return a Promise or a [node.js stream].\n\nie:\n\n```js\nconst tasks = {\n  cb_async: (cb) =\u003e {\n    setTimeout(cb, 10);\n  },\n  promise_async: () =\u003e {\n    return new Promise(resolve =\u003e {\n      setTimeout(resolve, 10);\n    }\n  }\n}\n```\n\n## Detailed Reference\n\nSee [reference](./REFERENCE.md) for more detailed information on features such as [load tasks into namespace], and setup [auto complete with namespace] for your shell.\n\n## License\n\nLicensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n\n[ci-shield]: https://travis-ci.org/electrode-io/xarc-run.svg?branch=master\n[ci-url]: https://travis-ci.org/electrode-io/xarc-run\n[npm-image]: https://badge.fury.io/js/%40xarc%2Frun.svg\n[npm-url]: https://npmjs.org/package/@xarc/run\n[daviddm-image]: https://david-dm.org/electrode-io/xarc-run/status.svg\n[daviddm-url]: https://david-dm.org/electrode-io/xarc-run\n[daviddm-dev-image]: https://david-dm.org/electrode-io/xarc-run/dev-status.svg\n[daviddm-dev-url]: https://david-dm.org/electrode-io/xarc-run?type=dev\n[npm scripts]: https://docs.npmjs.com/misc/scripts\n[@xarc/run-cli]: https://github.com/electrode-io/xarc-run-cli\n[bash]: https://www.gnu.org/software/bash/\n[zsh]: http://www.zsh.org/\n[load tasks into namespace]: REFERENCE.md#loading-task\n[auto complete with namespace]: REFERENCE.md#auto-complete-with-namespace\n[npm]: https://www.npmjs.com/package/npm\n[node.js stream]: https://nodejs.org/api/stream.html\n[coverage-image]: https://coveralls.io/repos/github/electrode-io/xarc-run/badge.svg?branch=master\n[coverage-url]: https://coveralls.io/github/electrode-io/xarc-run?branch=master\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectrode-io%2Fxarc-run","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felectrode-io%2Fxarc-run","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectrode-io%2Fxarc-run/lists"}