Skip to content

Commit

Permalink
Add cwd option to programmatic API (#246)
Browse files Browse the repository at this point in the history
Closes #216
  • Loading branch information
thdk committed Feb 20, 2021
1 parent 6fcbf19 commit 986bc3d
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 18 deletions.
20 changes: 13 additions & 7 deletions README.md
Expand Up @@ -10,11 +10,13 @@ Like `npm run watch-js & npm run watch-less` but better.
![](docs/demo.gif)

**Table of contents**
- [Why](#why)
- [Install](#install)
- [Usage](#usage)
- [Programmatic Usage](#programmatic-usage)
- [FAQ](#faq)
- [Concurrently](#concurrently)
- [Why](#why)
- [Install](#install)
- [Usage](#usage)
- [Programmatic Usage](#programmatic-usage)
- [`concurrently(commands[, options])`](#concurrentlycommands-options)
- [FAQ](#faq)

## Why

Expand Down Expand Up @@ -226,8 +228,10 @@ concurrently can be used programmatically by using the API documented below:

### `concurrently(commands[, options])`
- `commands`: an array of either strings (containing the commands to run) or objects
with the shape `{ command, name, prefixColor, env }`.
with the shape `{ command, name, prefixColor, env, cwd }`.
- `options` (optional): an object containing any of the below:
- `cwd`: the working directory to be used by all commands. Can be overriden per command.
Default: `process.cwd()`.
- `defaultInputTarget`: the default input target when reading from `inputStream`.
Default: `0`.
- `inputStream`: a [`Readable` stream](https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_readable_streams)
Expand Down Expand Up @@ -264,11 +268,13 @@ const concurrently = require('concurrently');
concurrently([
'npm:watch-*',
{ command: 'nodemon', name: 'server' },
{ command: 'deploy', name: 'deploy', env: { PUBLIC_KEY: '...' } }
{ command: 'deploy', name: 'deploy', env: { PUBLIC_KEY: '...' } },
{ command: 'watch', name: 'watch', cwd: path.resolve(__dirname, 'scripts/watchers')}
], {
prefix: 'name',
killOthers: ['failure', 'success'],
restartTries: 3,
cwd: path.resolve(__dirname, 'scripts'),
}).then(success, failure);
```

Expand Down
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -22,6 +22,7 @@ module.exports = (commands, options = {}) => {
maxProcesses: options.maxProcesses,
raw: options.raw,
successCondition: options.successCondition,
cwd: options.cwd,
controllers: [
new LogError({ logger }),
new LogOutput({ logger }),
Expand Down
9 changes: 7 additions & 2 deletions src/concurrently.js
Expand Up @@ -16,7 +16,8 @@ const defaults = {
spawn,
kill: treeKill,
raw: false,
controllers: []
controllers: [],
cwd: undefined,
};

module.exports = (commands, options) => {
Expand All @@ -37,7 +38,11 @@ module.exports = (commands, options) => {
.map((command, index) => new Command(
Object.assign({
index,
spawnOpts: getSpawnOpts({ raw: options.raw, env: command.env }),
spawnOpts: getSpawnOpts({
raw: options.raw,
env: command.env,
cwd: command.cwd || options.cwd,
}),
killProcess: options.kill,
spawn: options.spawn,
}, command)
Expand Down
35 changes: 31 additions & 4 deletions src/concurrently.spec.js
Expand Up @@ -104,15 +104,42 @@ it('merges extra env vars into each command', () => {
{ command: 'echo', env: { foo: 'baz' } },
'kill'
]);


expect(spawn).toHaveBeenCalledTimes(3);
expect(spawn).toHaveBeenCalledWith('echo', expect.objectContaining({
env: expect.objectContaining({ foo: 'bar' })
}));
expect(spawn).toHaveBeenCalledWith('echo', expect.objectContaining({
env: expect.objectContaining({ foo: 'baz' })
}));
expect(spawn).toHaveBeenCalledWith('kill', expect.objectContaining({
env: expect.not.objectContaining({ foo: expect.anything() })
}));
});

it('uses cwd from options for each command', () => {
create(
[
{ command: 'echo', env: { foo: 'bar' } },
{ command: 'echo', env: { foo: 'baz' } },
'kill'
],
{
cwd: 'foobar',
}
);

expect(spawn).toHaveBeenCalledTimes(3);
expect(spawn).toHaveBeenCalledWith('echo', expect.objectContaining({
env: expect.objectContaining({ foo: 'bar' })
env: expect.objectContaining({ foo: 'bar' }),
cwd: 'foobar',
}));
expect(spawn).toHaveBeenCalledWith('echo', expect.objectContaining({
env: expect.objectContaining({ foo: 'baz' })
env: expect.objectContaining({ foo: 'baz' }),
cwd: 'foobar',
}));
expect(spawn).toHaveBeenCalledWith('kill', expect.objectContaining({
env: expect.not.objectContaining({ foo: expect.anything() })
env: expect.not.objectContaining({ foo: expect.anything() }),
cwd: 'foobar',
}));
});
4 changes: 3 additions & 1 deletion src/defaults.js
Expand Up @@ -25,5 +25,7 @@ module.exports = {
// Condition of success for concurrently itself.
success: 'all',
// Refer to https://date-fns.org/v2.0.1/docs/format
timestampFormat: 'yyyy-MM-dd HH:mm:ss.SSS'
timestampFormat: 'yyyy-MM-dd HH:mm:ss.SSS',
// Current working dir passed as option to spawn command. Default: process.cwd()
cwd: undefined
};
7 changes: 5 additions & 2 deletions src/get-spawn-opts.js
Expand Up @@ -2,11 +2,14 @@ const supportsColor = require('supports-color');

module.exports = ({
colorSupport = supportsColor.stdout,
cwd,
process = global.process,
raw = false,
env = {}
env = {},
}) => Object.assign(
{},
{
cwd: cwd || process.cwd(),
},
raw && { stdio: 'inherit' },
/^win/.test(process.platform) && { detached: false },
{ env: Object.assign(colorSupport ? { FORCE_COLOR: colorSupport.level } : {}, process.env, env) }
Expand Down
16 changes: 14 additions & 2 deletions src/get-spawn-opts.spec.js
@@ -1,18 +1,30 @@
const getSpawnOpts = require('./get-spawn-opts');

it('sets detached mode to false for Windows platform', () => {
expect(getSpawnOpts({ process: { platform: 'win32' } }).detached).toBe(false);
expect(getSpawnOpts({ process: { platform: 'win32', cwd: jest.fn() } }).detached).toBe(false);
});

it('sets stdio to inherit when raw', () => {
expect(getSpawnOpts({ raw: true }).stdio).toBe('inherit');
});

it('merges FORCE_COLOR into env vars if color supported', () => {
const process = { env: { foo: 'bar' } };
const process = { env: { foo: 'bar' }, cwd: jest.fn() };
expect(getSpawnOpts({ process, colorSupport: false }).env).toEqual(process.env);
expect(getSpawnOpts({ process, colorSupport: { level: 1 } }).env).toEqual({
FORCE_COLOR: 1,
foo: 'bar'
});
});

it('sets default cwd to process.cwd()', () => {
const process = { cwd: jest.fn().mockReturnValue('process-cwd') };
expect(getSpawnOpts({
process,
}).cwd).toBe('process-cwd');
});

it('overrides default cwd', () => {
const cwd = 'foobar';
expect(getSpawnOpts({ cwd }).cwd).toBe(cwd);
});

0 comments on commit 986bc3d

Please sign in to comment.