Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0eef560f1d | |||
| f7f42ff36c | |||
| 77d2e6ee57 | |||
| e8bd8da3c7 | |||
| 91b3e273de | |||
| e6a7b352f3 | |||
| 8c1b306313 | |||
| a893e7a771 |
16
changelog.md
16
changelog.md
@@ -1,5 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-03 - 3.2.1 - fix(watcher)
|
||||||
|
ensure child processes are killed and awaited during shutdown; improve cleanup handlers; bump smartshell dependency to ^3.3.2
|
||||||
|
|
||||||
|
- Await child process kill() calls in restart and stop to avoid race conditions and ensure proper termination.
|
||||||
|
- Add last-resort synchronous SIGKILL in process 'exit' handler to terminate orphaned child processes.
|
||||||
|
- Make SIGINT and timeout handlers async and await stop() to perform a clean shutdown before exiting.
|
||||||
|
- Bump @push.rocks/smartshell from ^3.3.0 to ^3.3.2 in package.json.
|
||||||
|
|
||||||
|
## 2026-02-24 - 3.2.0 - feat(bundle)
|
||||||
|
add configurable bundle output modes and bundler options (support base64ts, production builds, includeFiles, maxLineLength) and route non-default outputs to a CustomBundleHandler
|
||||||
|
|
||||||
|
- Added new ITswatchConfig fields: outputMode, bundler, production, includeFiles, maxLineLength
|
||||||
|
- tswatch now creates a CustomBundleHandler and uses it when outputMode is not 'bundle' (e.g. base64ts)
|
||||||
|
- Default bundling path now reads bundler and production from bundleConfig (defaults to 'esbuild' and false)
|
||||||
|
- Bumped dependency @git.zone/tsbundle to ^2.9.0
|
||||||
|
|
||||||
## 2026-02-05 - 3.1.0 - feat(dev-server)
|
## 2026-02-05 - 3.1.0 - feat(dev-server)
|
||||||
add no-cache headers to built-in development server; update docs and bump dependencies
|
add no-cache headers to built-in development server; update docs and bump dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tswatch",
|
"name": "@git.zone/tswatch",
|
||||||
"version": "3.1.0",
|
"version": "3.2.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A development tool for automatically watching and re-compiling TypeScript projects upon detecting file changes, enhancing developer workflows.",
|
"description": "A development tool for automatically watching and re-compiling TypeScript projects upon detecting file changes, enhancing developer workflows.",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -24,18 +24,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedserver": "^8.3.0",
|
"@api.global/typedserver": "^8.3.0",
|
||||||
"@git.zone/tsbundle": "^2.8.3",
|
"@git.zone/tsbundle": "^2.9.0",
|
||||||
"@git.zone/tsrun": "^2.0.1",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@push.rocks/early": "^4.0.4",
|
"@push.rocks/early": "^4.0.4",
|
||||||
"@push.rocks/lik": "^6.2.2",
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/npmextra": "^5.3.3",
|
"@push.rocks/npmextra": "^5.3.3",
|
||||||
"@push.rocks/smartcli": "^4.0.20",
|
"@push.rocks/smartcli": "^4.0.20",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
|
"@push.rocks/smartexit": "^2.0.1",
|
||||||
"@push.rocks/smartfs": "^1.3.1",
|
"@push.rocks/smartfs": "^1.3.1",
|
||||||
"@push.rocks/smartinteract": "^2.0.16",
|
"@push.rocks/smartinteract": "^2.0.16",
|
||||||
"@push.rocks/smartlog": "^3.1.10",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
||||||
"@push.rocks/smartshell": "^3.3.0",
|
"@push.rocks/smartshell": "^3.3.4",
|
||||||
"@push.rocks/smartwatch": "^6.3.0",
|
"@push.rocks/smartwatch": "^6.3.0",
|
||||||
"@push.rocks/taskbuffer": "^4.2.0"
|
"@push.rocks/taskbuffer": "^4.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
4430
pnpm-lock.yaml
generated
4430
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tswatch',
|
name: '@git.zone/tswatch',
|
||||||
version: '3.1.0',
|
version: '3.2.1',
|
||||||
description: 'A development tool for automatically watching and re-compiling TypeScript projects upon detecting file changes, enhancing developer workflows.'
|
description: 'A development tool for automatically watching and re-compiling TypeScript projects upon detecting file changes, enhancing developer workflows.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ export interface IBundleConfig {
|
|||||||
watchPatterns?: string[];
|
watchPatterns?: string[];
|
||||||
/** If true, trigger server reload after bundling (default: true) */
|
/** If true, trigger server reload after bundling (default: true) */
|
||||||
triggerReload?: boolean;
|
triggerReload?: boolean;
|
||||||
|
/** Output mode: 'bundle' writes JS, 'base64ts' writes base64-encoded TS (default: 'bundle') */
|
||||||
|
outputMode?: 'bundle' | 'base64ts';
|
||||||
|
/** Bundler to use (default: 'esbuild') */
|
||||||
|
bundler?: 'esbuild' | 'rolldown' | 'rspack';
|
||||||
|
/** Whether to produce a production build (default: false) */
|
||||||
|
production?: boolean;
|
||||||
|
/** Files to include alongside the bundle */
|
||||||
|
includeFiles?: (string | { from: string; to: string })[];
|
||||||
|
/** Max chars per line for base64ts output. 0 or undefined = unlimited */
|
||||||
|
maxLineLength?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class TsWatch {
|
|||||||
public typedserver: plugins.typedserver.TypedServer | null = null;
|
public typedserver: plugins.typedserver.TypedServer | null = null;
|
||||||
|
|
||||||
private tsbundle = new plugins.tsbundle.TsBundle();
|
private tsbundle = new plugins.tsbundle.TsBundle();
|
||||||
|
private customBundleHandler = new plugins.tsbundle.CustomBundleHandler();
|
||||||
private htmlHandler = new plugins.tsbundle.HtmlHandler();
|
private htmlHandler = new plugins.tsbundle.HtmlHandler();
|
||||||
private assetsHandler = new plugins.tsbundle.AssetsHandler();
|
private assetsHandler = new plugins.tsbundle.AssetsHandler();
|
||||||
|
|
||||||
@@ -45,6 +46,14 @@ export class TsWatch {
|
|||||||
public async start() {
|
public async start() {
|
||||||
logger.log('info', 'Starting tswatch with config-driven mode');
|
logger.log('info', 'Starting tswatch with config-driven mode');
|
||||||
|
|
||||||
|
// Install global process lifecycle handlers (SIGINT, SIGTERM, etc.)
|
||||||
|
// This is the single authority for signal handling — no per-watcher handlers.
|
||||||
|
plugins.smartexit.ProcessLifecycle.install();
|
||||||
|
const exitInstance = new plugins.smartexit.SmartExit({ silent: true });
|
||||||
|
exitInstance.addCleanupFunction(async () => {
|
||||||
|
await this.stop();
|
||||||
|
});
|
||||||
|
|
||||||
// Start server if configured
|
// Start server if configured
|
||||||
if (this.config.server?.enabled) {
|
if (this.config.server?.enabled) {
|
||||||
await this.startServer();
|
await this.startServer();
|
||||||
@@ -128,10 +137,22 @@ export class TsWatch {
|
|||||||
} else if (fromPath.endsWith('/') || !fromPath.includes('.')) {
|
} else if (fromPath.endsWith('/') || !fromPath.includes('.')) {
|
||||||
// Assets directory copy
|
// Assets directory copy
|
||||||
await this.assetsHandler.processAssets();
|
await this.assetsHandler.processAssets();
|
||||||
|
} else if (bundleConfig.outputMode && bundleConfig.outputMode !== 'bundle') {
|
||||||
|
// Non-default outputMode (e.g. base64ts) — use CustomBundleHandler
|
||||||
|
await this.customBundleHandler.processSingleBundle({
|
||||||
|
from: bundleConfig.from,
|
||||||
|
to: bundleConfig.to,
|
||||||
|
outputMode: bundleConfig.outputMode,
|
||||||
|
bundler: bundleConfig.bundler || 'esbuild',
|
||||||
|
production: bundleConfig.production || false,
|
||||||
|
includeFiles: bundleConfig.includeFiles,
|
||||||
|
maxLineLength: bundleConfig.maxLineLength,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// TypeScript bundling
|
// Standard TypeScript bundling (default)
|
||||||
await this.tsbundle.build(paths.cwd, fromPath, toPath, {
|
await this.tsbundle.build(paths.cwd, fromPath, toPath, {
|
||||||
bundler: 'esbuild',
|
bundler: bundleConfig.bundler || 'esbuild',
|
||||||
|
production: bundleConfig.production || false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export class Watcher {
|
|||||||
if (this.options.commandToExecute) {
|
if (this.options.commandToExecute) {
|
||||||
if (this.currentExecution && this.options.restart) {
|
if (this.currentExecution && this.options.restart) {
|
||||||
logger.log('ok', `[${name}] restarting: ${this.options.commandToExecute}`);
|
logger.log('ok', `[${name}] restarting: ${this.options.commandToExecute}`);
|
||||||
this.currentExecution.kill();
|
await this.currentExecution.kill();
|
||||||
} else if (!this.currentExecution) {
|
} else if (!this.currentExecution) {
|
||||||
logger.log('ok', `[${name}] executing: ${this.options.commandToExecute}`);
|
logger.log('ok', `[${name}] executing: ${this.options.commandToExecute}`);
|
||||||
}
|
}
|
||||||
@@ -181,27 +181,14 @@ export class Watcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this method sets up a clean exit strategy
|
* Sets up timeout-based cleanup if configured.
|
||||||
|
* Signal handling (SIGINT/SIGTERM) is managed globally by ProcessLifecycle in TsWatch.
|
||||||
*/
|
*/
|
||||||
private async setupCleanup() {
|
private async setupCleanup() {
|
||||||
process.on('exit', () => {
|
|
||||||
console.log('');
|
|
||||||
console.log('now exiting!');
|
|
||||||
this.stop();
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
console.log('');
|
|
||||||
console.log('ok! got SIGINT We are exiting! Just cleaning up to exit neatly :)');
|
|
||||||
this.stop();
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle timeout
|
|
||||||
if (this.options.timeout) {
|
if (this.options.timeout) {
|
||||||
plugins.smartdelay.delayFor(this.options.timeout).then(() => {
|
plugins.smartdelay.delayFor(this.options.timeout).then(async () => {
|
||||||
console.log(`timed out afer ${this.options.timeout} milliseconds! exiting!`);
|
console.log(`timed out after ${this.options.timeout} milliseconds! exiting!`);
|
||||||
this.stop();
|
await this.stop();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -215,8 +202,10 @@ export class Watcher {
|
|||||||
clearTimeout(this.debounceTimer);
|
clearTimeout(this.debounceTimer);
|
||||||
}
|
}
|
||||||
await this.smartwatchInstance.stop();
|
await this.smartwatchInstance.stop();
|
||||||
if (this.currentExecution && !this.currentExecution.childProcess.killed) {
|
if (this.currentExecution) {
|
||||||
this.currentExecution.kill();
|
// Always tree-kill — even if the direct child is dead (.killed === true),
|
||||||
|
// grandchildren (e.g. tsrun, devserver) may still be running.
|
||||||
|
await this.currentExecution.kill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import * as lik from '@push.rocks/lik';
|
|||||||
import * as npmextra from '@push.rocks/npmextra';
|
import * as npmextra from '@push.rocks/npmextra';
|
||||||
import * as smartcli from '@push.rocks/smartcli';
|
import * as smartcli from '@push.rocks/smartcli';
|
||||||
import * as smartdelay from '@push.rocks/smartdelay';
|
import * as smartdelay from '@push.rocks/smartdelay';
|
||||||
|
import * as smartexit from '@push.rocks/smartexit';
|
||||||
import * as smartfs from '@push.rocks/smartfs';
|
import * as smartfs from '@push.rocks/smartfs';
|
||||||
import * as smartinteract from '@push.rocks/smartinteract';
|
import * as smartinteract from '@push.rocks/smartinteract';
|
||||||
import * as smartlog from '@push.rocks/smartlog';
|
import * as smartlog from '@push.rocks/smartlog';
|
||||||
@@ -29,6 +30,7 @@ export {
|
|||||||
npmextra,
|
npmextra,
|
||||||
smartcli,
|
smartcli,
|
||||||
smartdelay,
|
smartdelay,
|
||||||
|
smartexit,
|
||||||
smartfs,
|
smartfs,
|
||||||
smartinteract,
|
smartinteract,
|
||||||
smartlog,
|
smartlog,
|
||||||
|
|||||||
Reference in New Issue
Block a user