@lowdep/procfile-run 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/bin/procfile-run.js +288 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rushabh Shah
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# procfile-run
|
|
2
|
+
|
|
3
|
+
   
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Start all your dev services with one command. Reads a standard `Procfile` and runs every process in parallel with color-coded, labeled output. Zero dependencies.
|
|
7
|
+
|
|
8
|
+
Works on **Windows, Mac, and Linux** — unlike `foreman` (Ruby), `goreman`/`overmind` (Go binaries), or shell scripts.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g procfile-run
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or without installing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx procfile-run
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
Create a `Procfile`:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
web: node server.js
|
|
32
|
+
worker: node worker.js
|
|
33
|
+
redis: redis-server --port 6379
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
procfile-run
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Example Output
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
procfile-run Procfile
|
|
48
|
+
Starting 3 process(es)...
|
|
49
|
+
|
|
50
|
+
│ web started (pid 12345) node server.js
|
|
51
|
+
│ worker started (pid 12346) node worker.js
|
|
52
|
+
│ redis started (pid 12347) redis-server --port 6379
|
|
53
|
+
│ Press Ctrl+C to stop all processes
|
|
54
|
+
|
|
55
|
+
web │ Listening on port 3000
|
|
56
|
+
redis │ Ready to accept connections
|
|
57
|
+
worker │ Worker started, waiting for jobs...
|
|
58
|
+
worker │ Processing job #1
|
|
59
|
+
web │ GET /api/jobs 200 12ms
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
procfile-run # Run ./Procfile
|
|
68
|
+
procfile-run Procfile.dev # Use a different Procfile
|
|
69
|
+
procfile-run --only web,worker # Run specific processes
|
|
70
|
+
procfile-run --restart # Auto-restart crashed processes
|
|
71
|
+
procfile-run --timestamp # Prefix lines with time
|
|
72
|
+
procfile-run --env .env.local # Load a specific env file
|
|
73
|
+
procfile-run --no-env # Don't load any .env file
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Procfile Format
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
# This is a comment
|
|
82
|
+
web: node server.js
|
|
83
|
+
worker: node --env-file=.env worker.js
|
|
84
|
+
db: pg_ctl start -D /usr/local/var/postgresql@14
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Standard [Heroku Procfile](https://devcenter.heroku.com/articles/procfile) format — fully compatible with foreman, goreman, etc.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Environment Variables
|
|
92
|
+
|
|
93
|
+
By default, `procfile-run` loads `.env` from the same directory as the Procfile and merges it into each process's environment.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
procfile-run --env .env.staging # Load a specific env file
|
|
97
|
+
procfile-run --no-env # Skip .env loading
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Behavior on Exit
|
|
103
|
+
|
|
104
|
+
- If any process exits (success or crash), **all other processes are stopped** — unless `--restart` is active.
|
|
105
|
+
- `Ctrl+C` sends `SIGTERM` to all child processes cleanly.
|
|
106
|
+
- `--restart` will restart only *crashed* processes (non-zero exit), not ones that exited cleanly.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## vs. Alternatives
|
|
111
|
+
|
|
112
|
+
| Tool | Runtime needed | Windows | Zero-dep |
|
|
113
|
+
|---|---|---|---|
|
|
114
|
+
| `foreman` | Ruby gem | ❌ | ❌ |
|
|
115
|
+
| `goreman` | Go binary | ❌ | ❌ |
|
|
116
|
+
| `overmind` | Go binary | ❌ | ❌ |
|
|
117
|
+
| `concurrently` | npm (has deps) | ✓ | ❌ |
|
|
118
|
+
| `procfile-run` | Node.js | ✓ | ✓ |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Keywords
|
|
129
|
+
|
|
130
|
+
`procfile` · `foreman alternative` · `overmind alternative` · `goreman alternative` · `process manager` · `run multiple processes` · `dev servers` · `concurrently alternative` · `cross-platform` · `zero dependencies`
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
<div align="center">
|
|
135
|
+
|
|
136
|
+
**Built to solve, shared to help — Rushabh Shah 🛠️✨**
|
|
137
|
+
|
|
138
|
+
<sub>One of 40+ zero-dependency developer CLI tools — no <code>node_modules</code>, ever.</sub>
|
|
139
|
+
|
|
140
|
+
</div>
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const child_process = require('child_process');
|
|
7
|
+
|
|
8
|
+
const VERSION = '1.0.0';
|
|
9
|
+
|
|
10
|
+
// ─── ANSI palette (16 distinct colors for process labels) ─────────────────────
|
|
11
|
+
const isTTY = process.stdout.isTTY;
|
|
12
|
+
const c = (code, t) => isTTY ? `\x1b[${code}m${t}\x1b[0m` : t;
|
|
13
|
+
const bold = t => c('1', t);
|
|
14
|
+
const dim = t => c('2', t);
|
|
15
|
+
const red = t => c('31', t);
|
|
16
|
+
const green = t => c('32', t);
|
|
17
|
+
const yellow = t => c('33', t);
|
|
18
|
+
|
|
19
|
+
const PROC_COLORS = [
|
|
20
|
+
'36', '35', '33', '32', '34', '31', '96', '95', '93', '92', '94', '91',
|
|
21
|
+
'36;1', '35;1', '33;1', '32;1'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function procColor(idx, t) {
|
|
25
|
+
return isTTY ? `\x1b[${PROC_COLORS[idx % PROC_COLORS.length]}m${t}\x1b[0m` : t;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Procfile parser ───────────────────────────────────────────────────────────
|
|
29
|
+
function parseProcfile(content) {
|
|
30
|
+
const procs = [];
|
|
31
|
+
for (const line of content.split('\n')) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
34
|
+
const colon = trimmed.indexOf(':');
|
|
35
|
+
if (colon === -1) continue;
|
|
36
|
+
const name = trimmed.slice(0, colon).trim();
|
|
37
|
+
const cmd = trimmed.slice(colon + 1).trim();
|
|
38
|
+
if (name && cmd) procs.push({ name, cmd });
|
|
39
|
+
}
|
|
40
|
+
return procs;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── .env file loader ─────────────────────────────────────────────────────────
|
|
44
|
+
function loadEnvFile(filePath) {
|
|
45
|
+
if (!fs.existsSync(filePath)) return {};
|
|
46
|
+
const env = {};
|
|
47
|
+
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
50
|
+
const eq = trimmed.indexOf('=');
|
|
51
|
+
if (eq === -1) continue;
|
|
52
|
+
const key = trimmed.slice(0, eq).trim();
|
|
53
|
+
let val = trimmed.slice(eq + 1).trim();
|
|
54
|
+
if ((val.startsWith('"') && val.endsWith('"')) ||
|
|
55
|
+
(val.startsWith("'") && val.endsWith("'"))) {
|
|
56
|
+
val = val.slice(1, -1);
|
|
57
|
+
}
|
|
58
|
+
env[key] = val;
|
|
59
|
+
}
|
|
60
|
+
return env;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Shell-split a command string ─────────────────────────────────────────────
|
|
64
|
+
// Very basic: handles quoted args and space separation
|
|
65
|
+
function shellSplit(cmd) {
|
|
66
|
+
const parts = [];
|
|
67
|
+
let cur = '', inSingle = false, inDouble = false;
|
|
68
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
69
|
+
const ch = cmd[i];
|
|
70
|
+
if (ch === "'" && !inDouble) { inSingle = !inSingle; }
|
|
71
|
+
else if (ch === '"' && !inSingle) { inDouble = !inDouble; }
|
|
72
|
+
else if (ch === ' ' && !inSingle && !inDouble) {
|
|
73
|
+
if (cur) { parts.push(cur); cur = ''; }
|
|
74
|
+
} else { cur += ch; }
|
|
75
|
+
}
|
|
76
|
+
if (cur) parts.push(cur);
|
|
77
|
+
return parts;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
81
|
+
const args = process.argv.slice(2);
|
|
82
|
+
const VALUE_FLAGS = new Set(['--env', '--only']);
|
|
83
|
+
const positional = [];
|
|
84
|
+
for (let i = 0; i < args.length; i++) {
|
|
85
|
+
if (args[i].startsWith('-')) { if (VALUE_FLAGS.has(args[i])) i++; }
|
|
86
|
+
else positional.push(args[i]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getFlag(f) {
|
|
90
|
+
const i = args.indexOf(f);
|
|
91
|
+
return i !== -1 ? args[i + 1] : null;
|
|
92
|
+
}
|
|
93
|
+
function hasFlag(f) { return args.includes(f); }
|
|
94
|
+
|
|
95
|
+
if (hasFlag('--version') || hasFlag('-v')) {
|
|
96
|
+
console.log(`procfile-run v${VERSION}`); process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (hasFlag('--help') || hasFlag('-h')) {
|
|
100
|
+
console.log(`
|
|
101
|
+
${bold('procfile-run')} — Run all processes defined in a Procfile
|
|
102
|
+
|
|
103
|
+
${bold('USAGE')}
|
|
104
|
+
procfile-run [Procfile] [options]
|
|
105
|
+
|
|
106
|
+
${bold('OPTIONS')}
|
|
107
|
+
--only <names> Comma-separated list of process names to run
|
|
108
|
+
--env <file> Load additional env vars from a file (default: .env)
|
|
109
|
+
--no-env Don't load .env file
|
|
110
|
+
--restart Restart crashed processes automatically
|
|
111
|
+
--timestamp Prefix log lines with timestamp
|
|
112
|
+
--version Show version
|
|
113
|
+
|
|
114
|
+
${bold('EXAMPLES')}
|
|
115
|
+
procfile-run # Run all processes in ./Procfile
|
|
116
|
+
procfile-run Procfile.dev # Use a different Procfile
|
|
117
|
+
procfile-run --only web,worker # Run only web and worker
|
|
118
|
+
procfile-run --restart # Auto-restart on crash
|
|
119
|
+
|
|
120
|
+
${bold('PROCFILE FORMAT')}
|
|
121
|
+
# comment
|
|
122
|
+
web: node server.js
|
|
123
|
+
worker: node worker.js
|
|
124
|
+
redis: redis-server --port 6379
|
|
125
|
+
`);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Locate Procfile
|
|
130
|
+
const procfilePath = positional[0]
|
|
131
|
+
? path.resolve(positional[0])
|
|
132
|
+
: (() => {
|
|
133
|
+
for (const name of ['Procfile', 'procfile', 'PROCFILE', 'Procfile.dev']) {
|
|
134
|
+
const p = path.join(process.cwd(), name);
|
|
135
|
+
if (fs.existsSync(p)) return p;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
})();
|
|
139
|
+
|
|
140
|
+
if (!procfilePath || !fs.existsSync(procfilePath)) {
|
|
141
|
+
console.error(red(`\nNo Procfile found. Create a Procfile or specify one:\n procfile-run ./Procfile\n`));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const onlyFilter = getFlag('--only') ? new Set(getFlag('--only').split(',').map(s => s.trim())) : null;
|
|
146
|
+
const envFile = hasFlag('--no-env') ? null : (getFlag('--env') || path.join(path.dirname(procfilePath), '.env'));
|
|
147
|
+
const autoRestart = hasFlag('--restart');
|
|
148
|
+
const showTimestamp = hasFlag('--timestamp');
|
|
149
|
+
|
|
150
|
+
// Load env
|
|
151
|
+
const extraEnv = envFile ? loadEnvFile(envFile) : {};
|
|
152
|
+
const procEnv = { ...process.env, ...extraEnv };
|
|
153
|
+
|
|
154
|
+
// Parse Procfile
|
|
155
|
+
const allProcs = parseProcfile(fs.readFileSync(procfilePath, 'utf8'));
|
|
156
|
+
const procs = onlyFilter ? allProcs.filter(p => onlyFilter.has(p.name)) : allProcs;
|
|
157
|
+
|
|
158
|
+
if (!procs.length) {
|
|
159
|
+
console.error(yellow(`\nNo processes found in ${path.basename(procfilePath)}\n`));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Compute label width for aligned output
|
|
164
|
+
const labelWidth = Math.max(...procs.map(p => p.name.length));
|
|
165
|
+
|
|
166
|
+
function log(procIdx, procName, line) {
|
|
167
|
+
const label = procColor(procIdx, procName.padEnd(labelWidth));
|
|
168
|
+
const separator = dim('│');
|
|
169
|
+
const ts = showTimestamp ? dim(new Date().toLocaleTimeString() + ' ') : '';
|
|
170
|
+
process.stdout.write(`${label} ${separator} ${ts}${line}\n`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function logSystem(msg) {
|
|
174
|
+
console.log(dim(`${''.padEnd(labelWidth)} │ ${msg}`));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log(`\n${bold('procfile-run')} ${dim(path.basename(procfilePath))}`);
|
|
178
|
+
console.log(dim(` Starting ${procs.length} process(es)...\n`));
|
|
179
|
+
|
|
180
|
+
// ─── Process management ───────────────────────────────────────────────────────
|
|
181
|
+
const children = new Map(); // name → ChildProcess
|
|
182
|
+
let shuttingDown = false;
|
|
183
|
+
|
|
184
|
+
const isWin = process.platform === 'win32';
|
|
185
|
+
|
|
186
|
+
// On Windows, npm/npx/yarn/pnpm ship as .cmd wrappers — resolve them explicitly
|
|
187
|
+
// so we can avoid shell:true (which triggers a deprecation warning).
|
|
188
|
+
const WIN_CMD_SHIMS = new Set(['npm', 'npx', 'yarn', 'pnpm', 'tsc', 'ts-node', 'vite', 'next', 'jest', 'eslint', 'prettier']);
|
|
189
|
+
|
|
190
|
+
function resolveWinBin(bin) {
|
|
191
|
+
if (!isWin) return { bin, useShell: false };
|
|
192
|
+
const base = path.basename(bin, path.extname(bin)).toLowerCase();
|
|
193
|
+
if (WIN_CMD_SHIMS.has(base)) {
|
|
194
|
+
// Try to find the .cmd in node_modules/.bin or PATH
|
|
195
|
+
for (const dir of (process.env.PATH || '').split(';')) {
|
|
196
|
+
const candidate = path.join(dir, bin + '.cmd');
|
|
197
|
+
if (fs.existsSync(candidate)) return { bin: candidate, useShell: false };
|
|
198
|
+
}
|
|
199
|
+
// Fallback: cmd.exe /c handles .cmd resolution safely
|
|
200
|
+
return { bin: 'cmd.exe', useShell: false, prefix: ['/c', bin] };
|
|
201
|
+
}
|
|
202
|
+
return { bin, useShell: false };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function spawnProc(proc, idx) {
|
|
206
|
+
const parts = shellSplit(proc.cmd);
|
|
207
|
+
let bin = parts[0];
|
|
208
|
+
let argv = parts.slice(1);
|
|
209
|
+
|
|
210
|
+
if (isWin) {
|
|
211
|
+
const { bin: resolvedBin, prefix } = resolveWinBin(bin);
|
|
212
|
+
bin = resolvedBin;
|
|
213
|
+
if (prefix) argv = [...prefix, ...argv];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const child = child_process.spawn(bin, argv, {
|
|
217
|
+
env: procEnv,
|
|
218
|
+
cwd: path.dirname(procfilePath),
|
|
219
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
children.set(proc.name, child);
|
|
223
|
+
logSystem(`${procColor(idx, proc.name)} started (pid ${child.pid}) ${dim(proc.cmd)}`);
|
|
224
|
+
|
|
225
|
+
child.stdout.on('data', data => {
|
|
226
|
+
for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
|
|
227
|
+
log(idx, proc.name, line);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
child.stderr.on('data', data => {
|
|
232
|
+
for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
|
|
233
|
+
log(idx, proc.name, red(line));
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
child.on('exit', (code, signal) => {
|
|
238
|
+
children.delete(proc.name);
|
|
239
|
+
if (shuttingDown) return;
|
|
240
|
+
const reason = signal ? `signal ${signal}` : `exit code ${code}`;
|
|
241
|
+
if (code === 0) {
|
|
242
|
+
logSystem(`${procColor(idx, proc.name)} exited (${reason})`);
|
|
243
|
+
} else {
|
|
244
|
+
logSystem(red(`${proc.name} crashed (${reason})`));
|
|
245
|
+
}
|
|
246
|
+
if (autoRestart && !shuttingDown && code !== 0) {
|
|
247
|
+
logSystem(yellow(`${proc.name} restarting in 1s...`));
|
|
248
|
+
setTimeout(() => spawnProc(proc, idx), 1000);
|
|
249
|
+
} else if (!autoRestart && !shuttingDown) {
|
|
250
|
+
// If any process exits, kill the rest
|
|
251
|
+
logSystem(yellow(`${proc.name} exited — stopping all processes`));
|
|
252
|
+
shutdown(1);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
child.on('error', err => {
|
|
257
|
+
logSystem(red(`${proc.name} error: ${err.message}`));
|
|
258
|
+
if (!autoRestart) shutdown(1);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function shutdown(exitCode = 0) {
|
|
263
|
+
if (shuttingDown) return;
|
|
264
|
+
shuttingDown = true;
|
|
265
|
+
console.log();
|
|
266
|
+
logSystem('Stopping all processes...');
|
|
267
|
+
for (const [name, child] of children) {
|
|
268
|
+
try {
|
|
269
|
+
if (isWin) {
|
|
270
|
+
child_process.spawnSync('taskkill', ['/PID', String(child.pid), '/T', '/F']);
|
|
271
|
+
} else {
|
|
272
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
273
|
+
}
|
|
274
|
+
} catch { /* process may already be gone */ }
|
|
275
|
+
}
|
|
276
|
+
setTimeout(() => process.exit(exitCode), 500);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Catch CTRL+C
|
|
280
|
+
process.on('SIGINT', () => shutdown(0));
|
|
281
|
+
process.on('SIGTERM', () => shutdown(0));
|
|
282
|
+
|
|
283
|
+
// Start all processes
|
|
284
|
+
procs.forEach((proc, idx) => spawnProc(proc, idx));
|
|
285
|
+
|
|
286
|
+
console.log();
|
|
287
|
+
logSystem(dim(`Press Ctrl+C to stop all processes`));
|
|
288
|
+
console.log();
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lowdep/procfile-run",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Procfile process runner — start all your dev services with one command, zero dependencies",
|
|
5
|
+
"bin": {
|
|
6
|
+
"procfile-run": "bin/procfile-run.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"procfile",
|
|
10
|
+
"foreman",
|
|
11
|
+
"process-manager",
|
|
12
|
+
"cli",
|
|
13
|
+
"developer-tools",
|
|
14
|
+
"dev-server",
|
|
15
|
+
"heroku",
|
|
16
|
+
"foreman alternative",
|
|
17
|
+
"overmind alternative",
|
|
18
|
+
"goreman alternative",
|
|
19
|
+
"process manager",
|
|
20
|
+
"run multiple processes",
|
|
21
|
+
"dev servers",
|
|
22
|
+
"concurrently alternative",
|
|
23
|
+
"cross-platform",
|
|
24
|
+
"zero dependencies"
|
|
25
|
+
],
|
|
26
|
+
"author": "Rushabh Shah",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin/"
|
|
33
|
+
],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/Rushabh5000/procfile-run.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/Rushabh5000/procfile-run/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/Rushabh5000/procfile-run#readme",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|