├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── actions │ └── setup-pnpm │ │ └── action.yml └── workflows │ ├── lint-format.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── extensions.json └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin ├── concurrently.spec.ts ├── concurrently.ts ├── fixtures │ ├── read-echo.js │ └── sleep.mjs └── read-package.ts ├── declarations └── intl.d.ts ├── docs ├── README.md ├── cli │ ├── configuration.md │ ├── input-handling.md │ ├── output-control.md │ ├── passthrough-arguments.md │ ├── prefixing.md │ ├── restarting.md │ ├── shortcuts.md │ ├── success.md │ └── terminating.md └── demo.gif ├── index.d.mts ├── index.d.ts ├── index.js ├── index.mjs ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── src ├── assert.ts ├── command-parser │ ├── command-parser.ts │ ├── expand-arguments.spec.ts │ ├── expand-arguments.ts │ ├── expand-shortcut.spec.ts │ ├── expand-shortcut.ts │ ├── expand-wildcard.spec.ts │ ├── expand-wildcard.ts │ ├── strip-quotes.spec.ts │ └── strip-quotes.ts ├── command.spec.ts ├── command.ts ├── completion-listener.spec.ts ├── completion-listener.ts ├── concurrently.spec.ts ├── concurrently.ts ├── date-format.spec.ts ├── date-format.ts ├── defaults.ts ├── fixtures │ └── fake-command.ts ├── flow-control │ ├── flow-controller.ts │ ├── input-handler.spec.ts │ ├── input-handler.ts │ ├── kill-on-signal.spec.ts │ ├── kill-on-signal.ts │ ├── kill-others.spec.ts │ ├── kill-others.ts │ ├── log-error.spec.ts │ ├── log-error.ts │ ├── log-exit.spec.ts │ ├── log-exit.ts │ ├── log-output.spec.ts │ ├── log-output.ts │ ├── log-timings.spec.ts │ ├── log-timings.ts │ ├── logger-padding.spec.ts │ ├── logger-padding.ts │ ├── output-error-handler.spec.ts │ ├── output-error-handler.ts │ ├── restart-process.spec.ts │ ├── restart-process.ts │ ├── teardown.spec.ts │ └── teardown.ts ├── index.ts ├── jsonc.spec.ts ├── jsonc.ts ├── logger.spec.ts ├── logger.ts ├── observables.spec.ts ├── observables.ts ├── output-writer.spec.ts ├── output-writer.ts ├── prefix-color-selector.spec.ts ├── prefix-color-selector.ts ├── spawn.spec.ts └── spawn.ts ├── tests ├── .npmrc ├── cjs-import │ ├── package.json │ ├── smoke-test.ts │ └── tsconfig.json ├── cjs-require │ ├── package.json │ ├── smoke-test.ts │ └── tsconfig.json ├── esm │ ├── package.json │ ├── smoke-test.ts │ └── tsconfig.json ├── package.json ├── pnpm-lock.yaml └── smoke-tests.spec.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with newline ending and space indentation for all files 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | 12 | # 4 space indentation and max line length of 100 in JavaScript/TypeScript files 13 | [*.{mjs,js,ts}] 14 | indent_size = 4 15 | max_line_length = 100 16 | 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true 5 | }, 6 | "reportUnusedDisableDirectives": true, 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "ecmaVersion": 8 10 | }, 11 | "plugins": ["@typescript-eslint", "simple-import-sort", "import", "prettier"], 12 | "extends": [ 13 | "eslint:recommended", 14 | "plugin:@typescript-eslint/recommended", 15 | "prettier" 16 | ], 17 | "rules": { 18 | "simple-import-sort/imports": "error", 19 | "simple-import-sort/exports": "error", 20 | "import/first": "error", 21 | "import/newline-after-import": "error", 22 | "import/no-duplicates": "error", 23 | "curly": "error", 24 | "eqeqeq": ["error", "always", { "null": "ignore" }], 25 | "no-var": "error", 26 | "no-console": "error", 27 | "prefer-const": "error", 28 | "prefer-object-spread": "error", 29 | "prettier/prettier": ["error"] 30 | }, 31 | "overrides": [ 32 | { 33 | "files": ["*.spec.ts"], 34 | "plugins": ["jest"], 35 | "extends": ["plugin:jest/recommended"] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Thank you! <3 2 | 3 | github: [gustavohenke, paescuj] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /.github/actions/setup-pnpm/action.yml: -------------------------------------------------------------------------------- 1 | # See also https://github.com/actions/cache/blob/main/examples.md#node---npm 2 | 3 | name: Setup pnpm 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Install pnpm 8 | uses: pnpm/action-setup@v2 9 | with: 10 | version: 8 11 | 12 | - name: Get pnpm store directory 13 | id: pnpm-cache-dir 14 | shell: bash 15 | run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT 16 | 17 | - name: Setup pnpm cache 18 | uses: actions/cache@v3 19 | with: 20 | path: ${{ steps.pnpm-cache-dir.outputs.dir }} 21 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 22 | restore-keys: | 23 | ${{ runner.os }}-pnpm-store- 24 | -------------------------------------------------------------------------------- /.github/workflows/lint-format.yml: -------------------------------------------------------------------------------- 1 | name: Lint & Format 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | check: 17 | name: Check 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Node.js 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: 20 27 | 28 | - name: Setup pnpm 29 | uses: ./.github/actions/setup-pnpm 30 | 31 | - name: Install dependencies 32 | run: pnpm install && pnpm add --global concurrently 33 | 34 | - name: Lint & Format 35 | run: concurrently --prefix none --group "pnpm:lint" "pnpm:format" 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | gh-release: 13 | name: Create GitHub Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Create release 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | run: gh release create "$GITHUB_REF_NAME" --generate-notes 23 | 24 | publish-npm: 25 | name: Publish to NPM 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v3 33 | with: 34 | node-version: 20 35 | registry-url: https://registry.npmjs.org 36 | 37 | - name: Setup pnpm 38 | uses: ./.github/actions/setup-pnpm 39 | 40 | - name: Install dependencies 41 | run: pnpm install 42 | 43 | - name: Publish to NPM 44 | env: 45 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 46 | run: pnpm publish --no-git-checks 47 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**.md' 9 | - '**.ya?ml' 10 | - '!pnpm-lock.yaml' 11 | - '!.github/workflows/test.yml' 12 | pull_request: 13 | branches: 14 | - main 15 | paths-ignore: 16 | - '**.md' 17 | - '**.ya?ml' 18 | - '!pnpm-lock.yaml' 19 | - '!.github/workflows/test.yml' 20 | 21 | concurrency: 22 | group: ${{ github.workflow }}-${{ github.ref }} 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | test: 27 | name: Node.js ${{ matrix.node }} on ${{ matrix.os.name }} 28 | runs-on: ${{ matrix.os.version }} 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | node: 33 | - 18 34 | - 20 35 | - 22 36 | os: 37 | - name: Ubuntu 38 | version: ubuntu-latest 39 | - name: Windows 40 | version: windows-latest 41 | - name: macOS 42 | version: macOS-latest 43 | steps: 44 | - name: Checkout repository 45 | uses: actions/checkout@v4 46 | 47 | - name: Setup Node.js 48 | uses: actions/setup-node@v3 49 | with: 50 | node-version: ${{ matrix.node }} 51 | 52 | - name: Setup pnpm 53 | uses: ./.github/actions/setup-pnpm 54 | 55 | - name: Install dependencies 56 | run: pnpm install && pnpm add --global concurrently 57 | 58 | - name: Build & Unit Test 59 | run: concurrently --prefix none --group "pnpm:build" "pnpm:test" 60 | 61 | - name: Smoke Test 62 | # Don't collect coverage here as it overrides the unit test's coverage 63 | run: pnpm test:smoke --coverage=false 64 | 65 | - name: Submit coverage 66 | uses: coverallsapp/github-action@master 67 | continue-on-error: true 68 | with: 69 | github-token: ${{ secrets.github_token }} 70 | flag-name: Node.js ${{ matrix.node }} on ${{ matrix.os.name }} 71 | parallel: true 72 | 73 | coverage: 74 | name: Coverage 75 | needs: test 76 | runs-on: ubuntu-latest 77 | continue-on-error: true 78 | steps: 79 | - name: Finish coverage 80 | uses: coverallsapp/github-action@master 81 | with: 82 | github-token: ${{ secrets.github_token }} 83 | parallel-finished: true 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Outputs 2 | dist 3 | 4 | # Logs 5 | *.log 6 | 7 | # Coverage directory used by tools like istanbul 8 | coverage 9 | 10 | # Dependency directory 11 | node_modules 12 | 13 | # OS X 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | ./node_modules/.bin/lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Makes pnpm work between versions 7 and 8+ 2 | # https://github.com/pnpm/pnpm/issues/6649 3 | auto-install-peers=false 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | pnpm-lock.yaml 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "editorconfig.editorconfig", 6 | "orta.vscode-jest" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "jest.runMode": "watch", 3 | "jest.jestCommandLine": "pnpm test --", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": "explicit" 6 | }, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "editor.formatOnSave": true, 9 | "[javascript, typescript]": { 10 | "editor.formatOnSave": false 11 | }, 12 | "editor.rulers": [100], 13 | "typescript.tsdk": "node_modules/typescript/lib" 14 | } 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Pull requests and contributions are warmly welcome. 4 | Please follow existing code style and commit message conventions. Also remember to keep documentation 5 | updated. 6 | 7 | **Pull requests:** You don't need to bump version numbers or modify anything related to releasing. That stuff is fully automated, just write the functionality. 8 | 9 | # Maintaining 10 | 11 | ## Code Format & Linting 12 | 13 | Code format and lint checks are performed locally when committing to ensure the changes align with the configured rules of this repository. This happens with the help of the tools [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) and [lint-staged](https://github.com/okonet/lint-staged) which are automatically installed and configured on `pnpm install` (no further steps required). 14 | 15 | In case of problems, a corresponding message is displayed in your terminal. 16 | Please fix them and then run the commit command again. 17 | 18 | ## Test 19 | 20 | Tests can be executed with the following command: 21 | 22 | ```bash 23 | pnpm test 24 | ``` 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kimmo Brunfeldt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/fixtures/read-echo.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | process.stdin.on('data', (chunk) => { 4 | const line = chunk.toString().trim(); 5 | console.log(line); 6 | 7 | if (line === 'stop') { 8 | process.exit(0); 9 | } 10 | }); 11 | 12 | console.log('READING'); 13 | -------------------------------------------------------------------------------- /bin/fixtures/sleep.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Platform independent implementation of 'sleep' used as a command in tests 3 | * 4 | * (Windows doesn't provide the 'sleep' command by default, 5 | * see https://github.com/open-cli-tools/concurrently/issues/277) 6 | */ 7 | 8 | /* eslint-disable no-console */ 9 | 10 | const seconds = process.argv[2]; 11 | if (!seconds || isNaN(seconds) || process.argv.length > 3) { 12 | // Mimic behavior from native 'sleep' command 13 | console.error('usage: sleep seconds'); 14 | process.exit(1); 15 | } 16 | await new Promise((resolve) => setTimeout(resolve, seconds * 1000)); 17 | -------------------------------------------------------------------------------- /bin/read-package.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | 4 | /** 5 | * Traverses the directory tree until a package.json file is found. 6 | * 7 | * @throws if the root directory is reached, and no package.json is found. 8 | */ 9 | export function readPackage(): Record { 10 | let dir = require.main?.path ?? process.cwd(); 11 | let oldDir = dir; 12 | do { 13 | const pkgPath = path.join(dir, 'package.json'); 14 | if (fs.existsSync(pkgPath)) { 15 | return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); 16 | } 17 | 18 | oldDir = dir; 19 | dir = path.dirname(dir); 20 | } while (oldDir !== dir); 21 | 22 | throw new Error('package.json not found'); 23 | } 24 | -------------------------------------------------------------------------------- /declarations/intl.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | // TODO: Delete this file once Typescript has added these. 3 | declare namespace Intl { 4 | interface DateTimeFormatPartTypesRegistry { 5 | yearName: any; 6 | relatedYear: any; 7 | } 8 | 9 | /** 10 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo 11 | */ 12 | interface WeekInfo { 13 | firstDay: number; 14 | weekend: readonly number[]; 15 | minimalDays: number; 16 | } 17 | 18 | interface Locale { 19 | readonly weekInfo: WeekInfo; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Concurrently Documentation 2 | 3 | ## CLI 4 | 5 | These articles cover using concurrently through CLI: 6 | 7 | - [Prefixing](./cli/prefixing.md) 8 | - [Output Control](./cli/output-control.md) 9 | - [Success Conditions](./cli/success.md) 10 | - [Shortcuts](./cli/shortcuts.md) 11 | - [Terminating Commands](./cli/terminating.md) 12 | - [Restarting Commands](./cli/restarting.md) 13 | - [Input Handling](./cli/input-handling.md) 14 | - [Passthrough Arguments](./cli/passthrough-arguments.md) 15 | - [Configuration](./cli/configuration.md) 16 | -------------------------------------------------------------------------------- /docs/cli/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | You might want to configure concurrently to always have certain flags on. 4 | Any of concurrently's flags can be set via environment variables that are prefixed with `CONCURRENTLY_`. 5 | 6 | ```bash 7 | $ export CONCURRENTLY_KILL_OTHERS=true 8 | $ export CONCURRENTLY_HANDLE_INPUT=true 9 | # Equivalent to passing --kill-others and --handle-input 10 | $ concurrently nodemon "echo 'hey nodemon, you won't last long'" 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/cli/input-handling.md: -------------------------------------------------------------------------------- 1 | # Input Handling 2 | 3 | By default, concurrently doesn't send input to any commands it spawns.
4 | In the below example, typing `rs` to manually restart [nodemon](https://nodemon.io/) does nothing: 5 | 6 | ```bash 7 | $ concurrently 'nodemon' 'npm run watch-js' 8 | rs 9 | ``` 10 | 11 | To turn on input handling, it's necessary to set the `--handle-input`/`-i` flag.
12 | This will send `rs` to the first command: 13 | 14 | ```bash 15 | $ concurrently --handle-input 'nodemon' 'npm run watch-js' 16 | rs 17 | ``` 18 | 19 | To send input to a different command instead, it's possible to prefix the input with the command index, followed by a `:`.
20 | For example, the below sends `rs` to the second command: 21 | 22 | ```bash 23 | $ concurrently --handle-input 'npm run watch-js' 'nodemon' 24 | 1:rs 25 | ``` 26 | 27 | If the command has a name, it's also possible to target it using that command's name: 28 | 29 | ```bash 30 | $ concurrently --handle-input --names js,server 'npm run watch-js' 'nodemon' 31 | server:rs 32 | ``` 33 | 34 | It's also possible to change the default command that receives input.
35 | To do this, set the `--default-input-target` flag to a command's index or name. 36 | 37 | ```bash 38 | $ concurrently --handle-input --default-input-target 1 'npm run watch-js' 'nodemon' 39 | rs 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/cli/output-control.md: -------------------------------------------------------------------------------- 1 | # Output Control 2 | 3 | concurrently offers a few ways to control a command's output. 4 | 5 | ## Hiding 6 | 7 | A command's outputs (and all its events) can be hidden by using the `--hide` flag. 8 | 9 | ```bash 10 | $ concurrently --hide 0 'echo Hello there' 'echo General Kenobi!' 11 | [1] General Kenobi! 12 | [1] echo 'General Kenobi!' exited with code 0 13 | ``` 14 | 15 | ## Grouping 16 | 17 | It might be useful at times to make sure that the commands outputs are grouped together, while running them in parallel.
18 | This can be done with the `--group` flag. 19 | 20 | ```bash 21 | $ concurrently --group 'echo Hello there && sleep 2 && echo General Kenobi!' 'echo hi Star Wars fans' 22 | [0] Hello there 23 | [0] General Kenobi! 24 | [0] echo Hello there && sleep 2 && echo 'General Kenobi!' exited with code 0 25 | [1] hi Star Wars fans 26 | [1] echo hi Star Wars fans exited with code 0 27 | ``` 28 | 29 | ## No Colors 30 | 31 | When piping concurrently's outputs to another command or file, you might want to force it to not use colors, as these can break the other command's parsing, or reduce the legibility of the output in non-terminal environments. 32 | 33 | ```bash 34 | $ concurrently -c red,blue --no-color 'echo Hello there' 'echo General Kenobi!' 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/cli/passthrough-arguments.md: -------------------------------------------------------------------------------- 1 | # Passthrough Arguments 2 | 3 | If you have a shortcut for running a specific combination of commands through concurrently, 4 | you might need at some point to pass additional arguments/flags to some of these. 5 | 6 | For example, imagine you have in your `package.json` file scripts like this: 7 | 8 | ```jsonc 9 | { 10 | // ... 11 | "scripts": { 12 | "build:client": "tsc -p client", 13 | "build:server": "tsc -p server", 14 | "build": "concurrently npm:build:client npm:build:server" 15 | } 16 | } 17 | ``` 18 | 19 | If you wanted to run only either `build:server` or `build:client` with an additional `--noEmit` flag, 20 | you can do so with `npm run build:server -- --noEmit`, for example.
21 | However, if you want to do that while using concurrently, as `npm run build -- --noEmit` for example, 22 | you might find that concurrently actually parses `--noEmit` as its own flag, which does nothing, 23 | because it doesn't exist. 24 | 25 | To solve this, you can set the `--passthrough-arguments`/`-P` flag, which instructs concurrently to 26 | take everything after a `--` as additional arguments that are passed through to the input commands 27 | via a few placeholder styles: 28 | 29 | ## Single argument 30 | 31 | We can modify the original `build` script to pass a single additional argument/flag to a script by using 32 | a 1-indexed `{number}` placeholder to the command you want it to apply to: 33 | 34 | ```jsonc 35 | { 36 | // ... 37 | "scripts": { 38 | // ... 39 | "build": "concurrently -P 'npm:build:client -- {1}' npm:build:server --", 40 | "typecheck": "npm run build -- --noEmit" 41 | } 42 | } 43 | ``` 44 | 45 | With this, running `npm run typecheck` will pass `--noEmit` only to `npm run build:client`. 46 | 47 | ## All arguments 48 | 49 | In the original `build` example script, you're more likely to want to pass every additional argument/flag 50 | to your commands. This can be done with the `{@}` placeholder. 51 | 52 | ```jsonc 53 | { 54 | // ... 55 | "scripts": { 56 | // ... 57 | "build": "concurrently -P 'npm:build:client -- {@}' 'npm:build:server -- {@}' --", 58 | "typecheck": "npm run build -- --watch --noEmit" 59 | } 60 | } 61 | ``` 62 | 63 | In the above example, both `--watch` and `--noEmit` are passed to each command. 64 | 65 | ## All arguments, combined 66 | 67 | If for some reason you wish to combine all additional arguments into a single one, you can do that with the `{*}` placeholder, 68 | which wraps the arguments in quotes. 69 | 70 | ```jsonc 71 | { 72 | // ... 73 | "scripts": { 74 | // ... 75 | "build": "concurrently -P 'npm:build:client -- --outDir {*}/client' 'npm:build:server -- --outDir {*}/server' -- $(date)" 76 | } 77 | } 78 | ``` 79 | 80 | In the above example, the output of the `date` command, which looks like `Sun 1 Sep 2024 23:50:00 AEST` will be passed as a single string to the `--outDir` parameter of both commands. 81 | -------------------------------------------------------------------------------- /docs/cli/prefixing.md: -------------------------------------------------------------------------------- 1 | # Prefixing 2 | 3 | ## Prefix Styles 4 | 5 | concurrently will by default prefix each command's outputs with a zero-based index, wrapped in square brackets: 6 | 7 | ```bash 8 | $ concurrently 'echo Hello there' "echo 'General Kenobi!'" 9 | [0] Hello there 10 | [1] General Kenobi! 11 | [0] echo Hello there exited with code 0 12 | [1] echo 'General Kenobi!' exited with code 0 13 | ``` 14 | 15 | If you've given the commands names, they are used instead: 16 | 17 | ```bash 18 | $ concurrently --names one,two 'echo Hello there' "echo 'General Kenobi!'" 19 | [one] Hello there 20 | [two] General Kenobi! 21 | [one] echo Hello there exited with code 0 22 | [two] echo 'General Kenobi!' exited with code 0 23 | ``` 24 | 25 | There are other prefix styles available too: 26 | 27 | | Style | Description | 28 | | --------- | --------------------------------- | 29 | | `index` | Zero-based command's index | 30 | | `name` | The command's name | 31 | | `command` | The command's line | 32 | | `time` | Time of output | 33 | | `pid` | ID of the command's process (PID) | 34 | | `none` | No prefix | 35 | 36 | Any of these can be used by setting the `--prefix`/`-p` flag. For example: 37 | 38 | ```bash 39 | $ concurrently --prefix pid 'echo Hello there' 'echo General Kenobi!' 40 | [2222] Hello there 41 | [2223] General Kenobi! 42 | [2222] echo Hello there exited with code 0 43 | [2223] echo 'General Kenobi!' exited with code 0 44 | ``` 45 | 46 | It's also possible to have a prefix based on a template. Any of the styles listed above can be used by wrapping it in `{}`. 47 | Doing so will also remove the square brackets: 48 | 49 | ```bash 50 | $ concurrently --prefix '{index}-{pid}' 'echo Hello there' 'echo General Kenobi!' 51 | 0-2222 Hello there 52 | 1-2223 General Kenobi! 53 | 0-2222 echo Hello there exited with code 0 54 | 1-2223 echo 'General Kenobi!' exited with code 0 55 | ``` 56 | 57 | ## Prefix Colors 58 | 59 | By default, there are no colors applied to concurrently prefixes, and they just use whatever the terminal's defaults are. 60 | 61 | This can be changed by using the `--prefix-colors`/`-c` flag, which takes a comma-separated list of colors to use.
62 | The available values are color names (e.g. `green`, `magenta`, `gray`, etc), a hex value (such as `#23de43`), or `auto`, to automatically select a color. 63 | 64 | ```bash 65 | $ concurrently -c red,blue 'echo Hello there' 'echo General Kenobi!' 66 | ``` 67 | 68 |
69 | List of available color names 70 | 71 | - `black` 72 | - `blue` 73 | - `cyan` 74 | - `green` 75 | - `gray` 76 | - `magenta` 77 | - `red` 78 | - `white` 79 | - `yellow` 80 |
81 | 82 | Colors can take modifiers too. Several can be applied at once by prepending `..` and so on. 83 | 84 | ```bash 85 | $ concurrently -c red,bold.blue.dim 'echo Hello there' 'echo General Kenobi!' 86 | ``` 87 | 88 |
89 | List of available modifiers 90 | 91 | - `reset` 92 | - `bold` 93 | - `dim` 94 | - `hidden` 95 | - `inverse` 96 | - `italic` 97 | - `strikethrough` 98 | - `underline` 99 |
100 | 101 | A background color can be set in a similarly fashion. 102 | 103 | ```bash 104 | $ concurrently -c bgGray,red.bgBlack 'echo Hello there' 'echo General Kenobi!' 105 | ``` 106 | 107 |
108 | List of available background color names 109 | 110 | - `bgBlack` 111 | - `bgBlue` 112 | - `bgCyan` 113 | - `bgGreen` 114 | - `bgGray` 115 | - `bgMagenta` 116 | - `bgRed` 117 | - `bgWhite` 118 | - `bgYellow` 119 |
120 | 121 | ## Prefix Length 122 | 123 | When using the `command` prefix style, it's possible that it'll be too long.
124 | It can be limited by setting the `--prefix-length`/`-l` flag: 125 | 126 | ```bash 127 | $ concurrently -p command -l 10 'echo Hello there' 'echo General Kenobi!' 128 | [echo..here] Hello there 129 | [echo..bi!'] General Kenobi! 130 | [echo..here] echo Hello there exited with code 0 131 | [echo..bi!'] echo 'General Kenobi!' exited with code 0 132 | ``` 133 | 134 | It's also possible that some prefixes are too short, and you want all of them to have the same length.
135 | This can be done by setting the `--pad-prefix` flag: 136 | 137 | ```bash 138 | $ concurrently -n foo,barbaz --pad-prefix 'echo Hello there' 'echo General Kenobi!' 139 | [foo ] Hello there 140 | [foo ] echo Hello there exited with code 0 141 | [barbaz] General Kenobi! 142 | [barbaz] echo 'General Kenobi!' exited with code 0 143 | ``` 144 | 145 | > [!NOTE] 146 | > If using the `pid` prefix style in combination with [`--restart-tries`](./restarting.md), the length of the PID might grow, in which case all subsequent lines will match the new length.
147 | > This might happen, for example, if the PID was 99 and it's now 100. 148 | -------------------------------------------------------------------------------- /docs/cli/restarting.md: -------------------------------------------------------------------------------- 1 | # Restarting Commands 2 | 3 | Sometimes it's useful to have commands that exited with a non-zero status to restart automatically.
4 | concurrently lets you configure how many times you wish for such a command to restart through the `--restart-tries` flag: 5 | 6 | ```bash 7 | $ concurrently --restart-tries 2 'exit 1' 8 | [0] exit 1 exited with code 1 9 | [0] exit 1 restarted 10 | [0] exit 1 exited with code 1 11 | [0] exit 1 restarted 12 | [0] exit 1 exited with code 1 13 | ``` 14 | 15 | Sometimes, it might be interesting to have commands wait before restarting.
16 | To do this, simply set `--restart-after` to a the number of milliseconds you'd like to delay restarting. 17 | 18 | ```bash 19 | $ concurrently -p time --restart-tries 1 --restart-after 3000 'exit 1' 20 | [2024-09-01 23:43:55.871] exit 1 exited with code 1 21 | [2024-09-01 23:43:58.874] exit 1 restarted 22 | [2024-09-01 23:43:58.891] exit 1 exited with code 1 23 | ``` 24 | 25 | If a command is not having success spawning, you might want to instead apply an exponential back-off.
26 | Set `--restart-after exponential` to have commands respawn with a `2^N` seconds delay. 27 | 28 | ```bash 29 | $ concurrently -p time --restart-tries 3 --restart-after exponential 'exit 1' 30 | 31 | [2024-09-01 23:49:01.124] exit 1 exited with code 1 32 | [2024-09-01 23:49:02.127] exit 1 restarted 33 | [2024-09-01 23:49:02.139] exit 1 exited with code 1 34 | [2024-09-01 23:49:04.141] exit 1 restarted 35 | [2024-09-01 23:49:04.157] exit 1 exited with code 1 36 | [2024-09-01 23:49:08.158] exit 1 restarted 37 | [2024-09-01 23:49:08.174] exit 1 exited with code 1 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/cli/shortcuts.md: -------------------------------------------------------------------------------- 1 | # Command Shortcuts 2 | 3 | Package managers that execute scripts from a `package.json` or `deno.(json|jsonc)` file can be shortened when in concurrently.
4 | The following are supported: 5 | 6 | | Syntax | Expands to | 7 | | --------------- | --------------------- | 8 | | `npm: