Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
528a56c358 | |||
7fb2389e3a |
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-10-16 - 1.5.0 - feat(core)
|
||||||
|
Add cwd option and child-process execution for custom working directory; implement signal-forwarding child runner; update docs and bump package version to 1.4.0
|
||||||
|
|
||||||
|
- Introduce IRunOptions with cwd support to runPath/runCli
|
||||||
|
- When cwd is provided, runCli now spawns a child process (runInChildProcess) to execute the script in the specified working directory
|
||||||
|
- runInChildProcess preserves node execArgv, inherits env and stdio, forwards signals (SIGINT, SIGTERM, SIGHUP) and propagates child exit codes/signals
|
||||||
|
- Update README with documentation and examples for running scripts with a custom working directory and parallel execution
|
||||||
|
- Bump package version to 1.4.0
|
||||||
|
|
||||||
## 2025-10-13 - 1.3.4 - fix(docs)
|
## 2025-10-13 - 1.3.4 - fix(docs)
|
||||||
Update README with expanded docs and examples; add pnpm and CI tooling configs
|
Update README with expanded docs and examples; add pnpm and CI tooling configs
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tsrun",
|
"name": "@git.zone/tsrun",
|
||||||
"version": "1.3.4",
|
"version": "1.5.0",
|
||||||
"description": "run typescript programs efficiently",
|
"description": "run typescript programs efficiently",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
|
31
readme.md
31
readme.md
@@ -67,6 +67,8 @@ await runCli('./myScript.ts');
|
|||||||
|
|
||||||
🎯 **TypeScript Native** - Full TypeScript support with excellent IntelliSense.
|
🎯 **TypeScript Native** - Full TypeScript support with excellent IntelliSense.
|
||||||
|
|
||||||
|
🔀 **Custom Working Directory** - Execute scripts with different cwds for parallel multi-project workflows.
|
||||||
|
|
||||||
## Why tsrun?
|
## Why tsrun?
|
||||||
|
|
||||||
Sometimes you just want to run a TypeScript file without setting up a build pipeline, configuring webpack, or waiting for `tsc` to compile. That's where tsrun shines:
|
Sometimes you just want to run a TypeScript file without setting up a build pipeline, configuring webpack, or waiting for `tsc` to compile. That's where tsrun shines:
|
||||||
@@ -141,6 +143,35 @@ for (const script of scripts) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Running with Custom Working Directory
|
||||||
|
|
||||||
|
Execute TypeScript files with a different working directory using the `cwd` option. This is especially useful for parallel execution across multiple projects:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { runPath } from '@git.zone/tsrun';
|
||||||
|
|
||||||
|
// Run with custom cwd
|
||||||
|
await runPath('./build.ts', undefined, { cwd: '/path/to/project-a' });
|
||||||
|
|
||||||
|
// Parallel execution with different cwds (safe and isolated)
|
||||||
|
await Promise.all([
|
||||||
|
runPath('./deploy.ts', undefined, { cwd: '/projects/frontend' }),
|
||||||
|
runPath('./deploy.ts', undefined, { cwd: '/projects/backend' }),
|
||||||
|
runPath('./deploy.ts', undefined, { cwd: '/projects/api' })
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- When `cwd` is provided, the script executes in a **child process** for complete isolation
|
||||||
|
- Without `cwd`, execution happens **in-process** (faster, less overhead)
|
||||||
|
- Child processes inherit all environment variables and stdio connections
|
||||||
|
- Perfect for running the same script across multiple project directories
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- Output from parallel executions may interleave on the console
|
||||||
|
- Each child process runs with its own isolated working directory
|
||||||
|
- Exit codes and signals are properly forwarded
|
||||||
|
|
||||||
## Package Information
|
## Package Information
|
||||||
|
|
||||||
- **npmjs**: [@git.zone/tsrun](https://www.npmjs.com/package/@git.zone/tsrun)
|
- **npmjs**: [@git.zone/tsrun](https://www.npmjs.com/package/@git.zone/tsrun)
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsrun',
|
name: '@git.zone/tsrun',
|
||||||
version: '1.3.4',
|
version: '1.5.0',
|
||||||
description: 'run typescript programs efficiently'
|
description: 'run typescript programs efficiently'
|
||||||
}
|
}
|
||||||
|
75
ts/index.ts
75
ts/index.ts
@@ -1,14 +1,24 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
const __dirname = plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url));
|
const __dirname = plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const runPath = async (pathArg: string, fromFileUrl?: string) => {
|
export interface IRunOptions {
|
||||||
|
cwd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const runPath = async (pathArg: string, fromFileUrl?: string, options?: IRunOptions) => {
|
||||||
pathArg = fromFileUrl
|
pathArg = fromFileUrl
|
||||||
? plugins.path.join(plugins.path.dirname(plugins.url.fileURLToPath(fromFileUrl)), pathArg)
|
? plugins.path.join(plugins.path.dirname(plugins.url.fileURLToPath(fromFileUrl)), pathArg)
|
||||||
: pathArg;
|
: pathArg;
|
||||||
await runCli(pathArg);
|
await runCli(pathArg, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runCli = async (pathArg?: string) => {
|
export const runCli = async (pathArg?: string, options?: IRunOptions) => {
|
||||||
|
// CRITICAL: Branch BEFORE splicing argv to avoid corruption
|
||||||
|
if (options?.cwd) {
|
||||||
|
return runInChildProcess(pathArg, options.cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing in-process execution
|
||||||
// contents of argv array
|
// contents of argv array
|
||||||
// process.argv[0] -> node Executable
|
// process.argv[0] -> node Executable
|
||||||
// process.argv[1] -> tsrun executable
|
// process.argv[1] -> tsrun executable
|
||||||
@@ -26,3 +36,62 @@ export const runCli = async (pathArg?: string) => {
|
|||||||
const unregister = tsx.register();
|
const unregister = tsx.register();
|
||||||
await import(absolutePathToTsFile);
|
await import(absolutePathToTsFile);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const runInChildProcess = async (pathArg: string | undefined, cwd: string): Promise<void> => {
|
||||||
|
const { spawn } = await import('child_process');
|
||||||
|
|
||||||
|
// Resolve cli.child.js relative to this file
|
||||||
|
const cliChildPath = plugins.path.join(__dirname, '../cli.child.js');
|
||||||
|
|
||||||
|
// Build args: [Node flags, entry point, script path, script args]
|
||||||
|
const args = [
|
||||||
|
...process.execArgv, // Preserve --inspect, etc.
|
||||||
|
cliChildPath,
|
||||||
|
...process.argv.slice(2) // Original CLI args (not spliced)
|
||||||
|
];
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const child = spawn(process.execPath, args, {
|
||||||
|
cwd: cwd,
|
||||||
|
env: process.env,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: false,
|
||||||
|
windowsHide: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal forwarding with cleanup
|
||||||
|
const signalHandler = (signal: NodeJS.Signals) => {
|
||||||
|
try { child.kill(signal); } catch {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
||||||
|
signals.forEach(sig => process.on(sig, signalHandler));
|
||||||
|
|
||||||
|
child.on('error', (err) => {
|
||||||
|
signals.forEach(sig => process.off(sig, signalHandler));
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code, signal) => {
|
||||||
|
// Clean up signal handlers
|
||||||
|
signals.forEach(sig => process.off(sig, signalHandler));
|
||||||
|
|
||||||
|
if (signal) {
|
||||||
|
// Child was terminated by signal
|
||||||
|
// On POSIX: try to exit with same signal
|
||||||
|
// On Windows: exit with convention (128 + signal number)
|
||||||
|
try {
|
||||||
|
process.kill(process.pid, signal);
|
||||||
|
} catch {
|
||||||
|
// Fallback to exit code
|
||||||
|
const signalExitCode = signal === 'SIGINT' ? 130 : 128;
|
||||||
|
process.exit(signalExitCode);
|
||||||
|
}
|
||||||
|
} else if (code !== null && code !== 0) {
|
||||||
|
process.exit(code);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Reference in New Issue
Block a user