add websocket server to this dir. fix stuff for client

This commit is contained in:
2024-10-30 15:12:52 -05:00
parent f8152c6db8
commit 4886bc5b1f
3058 changed files with 1201180 additions and 2 deletions

View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) Gil Amran
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,128 @@
[![Build Status](https://travis-ci.com/gilamran/tsc-watch.svg?branch=master)](https://travis-ci.com/gilamran/tsc-watch)
# The nodemon for TypeScript
`tsc-watch` starts the installed TypeScript compiler (`tsc`) with `--watch` parameter, with the ability to react to compilation status.
`tsc-watch` was created to allow an easy dev process with TypeScript. Commonly used to restart a node server, similar to nodemon but for TypeScript.
**Anything that you can do with `tsc` you can do with `tsc-watch`, the only difference is that `tsc-watch` can react to compilation status.**
| Argument | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--onSuccess COMMAND` | Executes `COMMAND` on **every successful** compilation. |
| `--onFirstSuccess COMMAND` | Executes `COMMAND` on the **first successful** compilation. |
| `--onEmit COMMAND` | Executes debounced `COMMAND` on **every emitted file**, ignoring unchanged files and disregards compilation success or failure. |
| `--onEmitDebounceMs DELAY` | Delay by which to debounce `--onEmit` (default: 300). |
| `--onFailure COMMAND` | Executes `COMMAND` on **every failed** compilation. |
| `--onCompilationStarted COMMAND` | Executes `COMMAND` on **every compilation start** event (initial and incremental). |
| `--onCompilationComplete COMMAND` | Executes `COMMAND` on **every successful or failed** compilation. |
| `--maxNodeMem` | Calls `node` with a specific memory limit `max_old_space_size`, to use if your project needs more memory. |
| `--noColors` | By default tsc-watch adds colors the output with green<br>on success, and in red on failure. <br>Add this argument to prevent that. |
| `--noClear` | In watch mode the `tsc` compiler clears the screen before reporting<br>Add this argument to prevent that. |
| `--signalEmittedFiles` | Will run `tsc` compiler with `--listEmittedFiles`, but hiding TSFILE lines. Use it to enable `file_emitted` event, while keeping tsc stdout silent. |
| `--silent` | Do not print any messages on stdout. |
| `--compiler PATH` | The `PATH` will be used instead of typescript compiler.<br>Default is `typescript/bin/tsc` |
Notes:
- That all the above `COMMAND`s will be killed on process exit. (Using `SIGTERM`)
- A `COMMAND` is a single command and not multi command like `script1.sh && script2.sh`
- Any child process (`COMMAND`) will be terminated before creating a new one.
## Install
```sh
npm install tsc-watch --save-dev
```
```sh
## for command-line usage
npm install -g typescript tsc-watch
```
## Usage
### From Command-Line
```sh
## Watching a project (with tsconfig.json)
tsc-watch --onSuccess "node ./dist/server.js"
## Beep on failure
tsc-watch --onFailure "echo Beep! Compilation Failed"
## Watching a single file
tsc-watch server.ts --outDir ./dist --onSuccess "node ./dist/server.js"
## Custom compiler
tsc-watch --onSuccess "node ./dist/server.js" --compiler my-typescript/bin/tsc
```
### From npm script
```
"dev-server": "tsc-watch --noClear -p ./src/tsconfig.json --onSuccess \"node ./dist/server.js\"",
```
### From javascript
You can see a detailed example [here](https://github.com/gilamran/tsc-watch/blob/master/tsc-watch-client-example.js)
The client is implemented as an instance of `Node.JS`'s `EventEmitter`, with the following events:
- `started` - Emitted upon the compilation start (initial or incremental).
- `first_success` - Emitted upon first successful compilation.
- `subsequent_success` - Emitted upon every subsequent successful compilation.
- `compile_errors` - Emitted upon every failing compilation.
- `file_emitted` - Emitted upon every file transpiled if `--listEmittedFiles` is used.
Once subscribed to the relevant events, start the client by running `watch.start()`
To kill the client, run `watch.kill()`
Example usage:
```javascript
// Using CommonJS:
const { TscWatchClient } = require('tsc-watch/client');
// Using ES6 import:
import { TscWatchClient } from 'tsc-watch/client';
const watch = new TscWatchClient();
watch.on('started', () => {
console.log('Compilation started');
});
watch.on('first_success', () => {
console.log('First success!');
});
watch.on('success', () => {
// Your code goes here...
});
watch.on('compile_errors', () => {
// Your code goes here...
});
watch.start('--project', '.');
try {
// do something...
} catch (e) {
watch.kill(); // Fatal error, kill the compiler instance.
}
```
Notes:
- The (`onSuccess`) `COMMAND` will not run if the compilation failed.
- The (`onEmit`) `COMMAND` will not run if the compilation succeeded with no changed files, unless it is the first success.
- The (`onEmit`) `COMMAND` will run even if the compilation failed, but emitted changed files.
- The (`onEmit`) `COMMAND` will not run 100 times for 100 files, due to `--onEmitDebounce`
- The (`onEmit`) `COMMAND` is not cancelling the `onSuccess`/`onFirstSuccess`/`onFailure`/`onCompilationComplete`/`onCompilationStarted` commands and vice versa.
- `tsc-watch` is using the currently installed TypeScript compiler.
- `tsc-watch` is not changing the compiler, just adds the new arguments, compilation is the same, and all other arguments are the same.

View File

@ -0,0 +1 @@
export * from "./dist/client"

View File

@ -0,0 +1 @@
module.exports = require('./dist/client');

View File

@ -0,0 +1,15 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
export declare class TscWatchClient extends EventEmitter {
private tscWatchPath;
private tsc;
constructor(tscWatchPath?: string);
start(...args: string[]): void;
kill(): void;
runOnCompilationStartedCommand(): void;
runOnCompilationCompleteCommand(): void;
runOnFirstSuccessCommand(): void;
runOnFailureCommand(): void;
runOnSuccessCommand(): void;
runOnEmitCommand(): void;
}

View File

@ -0,0 +1,90 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TscWatchClient = void 0;
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const events_1 = require("events");
const tscWatchLibPath = path.join(__dirname, '..', 'lib', 'tsc-watch');
class TscWatchClient extends events_1.EventEmitter {
constructor(tscWatchPath = tscWatchLibPath) {
super();
this.tscWatchPath = tscWatchPath;
}
start(...args) {
const options = { stdio: 'inherit' };
this.tsc = (0, child_process_1.fork)(this.tscWatchPath, args, options);
this.tsc.on('message', (msg) => {
this.emit(...deserializeTscMessage(msg));
});
this.tsc.on('exit', (code, signal) => {
this.emit('exit', code, signal);
});
}
kill() {
if (this.tsc && this.tsc.kill) {
this.tsc.kill();
}
this.removeAllListeners();
}
runOnCompilationStartedCommand() {
if (this.tsc) {
this.tsc.send('run-on-compilation-started-command');
}
}
runOnCompilationCompleteCommand() {
if (this.tsc) {
this.tsc.send('run-on-compilation-complete-command');
}
}
runOnFirstSuccessCommand() {
if (this.tsc) {
this.tsc.send('run-on-first-success-command');
}
}
runOnFailureCommand() {
if (this.tsc) {
this.tsc.send('run-on-failure-command');
}
}
runOnSuccessCommand() {
if (this.tsc) {
this.tsc.send('run-on-success-command');
}
}
runOnEmitCommand() {
if (this.tsc) {
this.tsc.send('run-on-emit-command');
}
}
}
exports.TscWatchClient = TscWatchClient;
function deserializeTscMessage(strMsg) {
const indexOfSeparator = strMsg.indexOf(':');
if (indexOfSeparator === -1) {
return [strMsg];
}
return [strMsg.substring(0, indexOfSeparator), strMsg.substring(indexOfSeparator + 1)];
}

View File

@ -0,0 +1 @@
export * from './client';

View File

@ -0,0 +1,17 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./client"), exports);

View File

@ -0,0 +1 @@
"use strict";

View File

@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractArgs = exports.hasWatchCommand = exports.isCommandExist = void 0;
function removeRunnerArgs(args) {
return args.splice(2); // removing "node tsc-watch.js"
}
function getCommandIdx(args, command) {
const lowerCasedCommand = command.toLowerCase();
return args.map(arg => arg.toLowerCase()).indexOf(lowerCasedCommand);
}
function isCommandExist(args, command) {
return getCommandIdx(args, command) > 0;
}
exports.isCommandExist = isCommandExist;
function hasWatchCommand(args) {
return isCommandExist(args, '-w') || isCommandExist(args, '--watch');
}
exports.hasWatchCommand = hasWatchCommand;
function forceWatch(args) {
if (!hasWatchCommand(args)) {
args.push('--watch');
}
return args;
}
function extractCommandWithValue(args, command) {
let commandValue = null;
let commandIdx = getCommandIdx(args, command);
if (commandIdx > -1) {
commandValue = args[commandIdx + 1];
args.splice(commandIdx, 2);
}
return commandValue;
}
function extractCommand(args, command) {
let commandIdx = getCommandIdx(args, command);
if (commandIdx > -1) {
args.splice(commandIdx, 1);
return true;
}
return false;
}
function extractArgs(inputArgs) {
const args = forceWatch(removeRunnerArgs(inputArgs));
const onFirstSuccessCommand = extractCommandWithValue(args, '--onFirstSuccess');
const onSuccessCommand = extractCommandWithValue(args, '--onSuccess');
const onFailureCommand = extractCommandWithValue(args, '--onFailure');
const onEmitCommand = extractCommandWithValue(args, '--onEmit');
const onEmitDebounceMs = Number(extractCommandWithValue(args, '--onEmitDebounceMs')) || 300;
const onCompilationStarted = extractCommandWithValue(args, '--onCompilationStarted');
const onCompilationComplete = extractCommandWithValue(args, '--onCompilationComplete');
const maxNodeMem = extractCommandWithValue(args, '--maxNodeMem');
const noColors = extractCommand(args, '--noColors');
const noClear = extractCommand(args, '--noClear');
const silent = extractCommand(args, '--silent');
const signalEmittedFiles = extractCommand(args, '--signalEmittedFiles');
const requestedToListEmittedFiles = extractCommand(args, '--listEmittedFiles');
let compiler = extractCommandWithValue(args, '--compiler');
if (!compiler) {
compiler = 'typescript/bin/tsc';
}
else {
compiler = require.resolve(compiler, { paths: [process.cwd()] });
}
if (signalEmittedFiles || requestedToListEmittedFiles) {
if (args[0] === '--build' || args[0] === '-b') {
// TS6369: Option '--build' must be the first command line argument.
args.splice(1, 0, '--listEmittedFiles');
}
else {
args.unshift('--listEmittedFiles');
}
}
return {
onFirstSuccessCommand,
onSuccessCommand,
onFailureCommand,
onEmitCommand,
onEmitDebounceMs,
onCompilationStarted,
onCompilationComplete,
maxNodeMem,
noColors,
noClear,
requestedToListEmittedFiles,
signalEmittedFiles,
silent,
compiler,
args,
};
}
exports.extractArgs = extractArgs;

View File

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.debounce = void 0;
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
timer && clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
exports.debounce = debounce;

View File

@ -0,0 +1,38 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.kill = void 0;
const ps_tree_1 = __importDefault(require("ps-tree"));
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const child_process_1 = require("child_process");
let KILL_SIGNAL = '15'; // SIGTERM
let hasPS = true;
const isWindows = process.platform === 'win32';
// discover if the OS has `ps`, and therefore can use psTree
(0, child_process_1.exec)('ps', function (error) {
if (error) {
hasPS = false;
}
});
function kill(child) {
return new Promise((resolve) => {
if (isWindows) {
(0, child_process_1.exec)(`taskkill /pid ${child.pid} /T /F`, () => resolve());
}
else {
if (hasPS) {
(0, ps_tree_1.default)(child.pid, (err, kids) => {
const kidsPIDs = kids.map((p) => p.PID);
const args = [`-${KILL_SIGNAL}`, child.pid.toString(), ...kidsPIDs];
(0, cross_spawn_1.default)('kill', args).on('close', resolve);
});
}
else {
(0, child_process_1.exec)(`kill -${KILL_SIGNAL} ${child.pid}`, () => resolve());
}
}
});
}
exports.kill = kill;

View File

@ -0,0 +1,23 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const string_argv_1 = __importDefault(require("string-argv"));
const killer_1 = require("./killer");
function runCommand(fullCommand) {
const parts = (0, string_argv_1.default)(fullCommand);
const exec = parts[0];
const args = parts.splice(1);
return (0, cross_spawn_1.default)(exec, args, {
stdio: 'inherit',
});
}
function run(command) {
const process = runCommand(command);
const exitPromise = new Promise((resolve) => process.on('exit', resolve));
return () => Promise.all([(0, killer_1.kill)(process), exitPromise]);
}
exports.run = run;

View File

@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectState = exports.manipulate = exports.deleteClear = exports.print = void 0;
const ANSI_REGEX = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))', 'g');
const stripAnsi = (str) => str.replace(ANSI_REGEX, '');
const tscUsageSyntaxRegex = / -w, --watch.*Watch input files\./;
const typescriptPrettyErrorRegex = /:\d+:\d+ \- error TS\d+: /;
const typescriptErrorRegex = /\(\d+,\d+\): error TS\d+: /;
const typescriptEmittedFileRegex = /(TSFILE:)\s*(.*)/;
const compilationCompleteWithErrorRegex = / Found [^0][0-9]* error[s]?\. Watching for file changes\./;
const compilationCompleteWithoutErrorRegex = / Found 0 errors\. Watching for file changes\./;
const compilationCompleteRegex = /( Compilation complete\. Watching for file changes\.| Found \d+ error[s]?\. Watching for file changes\.)/;
const compilationStartedRegex = /( Starting compilation in watch mode\.\.\.| File change detected\. Starting incremental compilation\.\.\.)/;
const newAdditionToSyntax = [
' -w, --watch Watch input files. [always on]',
' --onSuccess COMMAND Executes `COMMAND` on **every successful** compilation.',
' --onFirstSuccess COMMAND Executes `COMMAND` on the **first successful** compilation.',
' --onFailure COMMAND Executes `COMMAND` on **every failed** compilation.',
' --onEmit COMMAND Executes debounced `COMMAND` on **every emitted file**, ignoring unchanged files and disregards compilation success or failure.',
' --onEmitDebounceMs DELAY Delay by which to debounce `--onEmit` (default: 300).',
' --onCompilationStarted COMMAND Executes `COMMAND` on **every compilation start** event.',
' --onCompilationComplete COMMAND Executes `COMMAND` on **every successful or failed** compilation.',
' --noColors Removes the red/green colors from the compiler output',
' --noClear Prevents the compiler from clearing the screen',
' --compiler PATH The PATH will be used instead of typescript compiler. Defaults typescript/bin/tsc.',
].join('\n');
function color(line, noClear = false) {
// coloring errors:
line = line.replace(typescriptErrorRegex, (m) => `\u001B[36m${m}\u001B[39m`); // Cyan
line = line.replace(typescriptPrettyErrorRegex, (m) => `\u001B[36m${m}\u001B[39m`); // Cyan
// completed with error:
line = line.replace(compilationCompleteWithErrorRegex, (m) => `\u001B[31m${m}\u001B[39m`); // Red
// completed without error:
line = line.replace(compilationCompleteWithoutErrorRegex, (m) => `\u001B[32m${m}\u001B[39m`); // Green
// usage
line = line.replace(tscUsageSyntaxRegex, (m) => `\u001B[33m${m}\u001B[39m`); // Yellow
if (noClear && compilationStartedRegex.test(line)) {
return '\n\n----------------------\n' + line;
}
return line;
}
function print(line, { noColors = false, noClear = false, requestedToListEmittedFiles = false, signalEmittedFiles = false, } = {}) {
if (signalEmittedFiles && !requestedToListEmittedFiles && line.startsWith('TSFILE:')) {
return;
}
console.log(noColors ? line : color(line, noClear));
}
exports.print = print;
function deleteClear(line) {
const buffer = Buffer.from(line);
if (buffer.length >= 2 && buffer[0] === 0x1b && buffer[1] === 0x63) {
return line.substr(2);
}
return line;
}
exports.deleteClear = deleteClear;
function manipulate(line) {
return line.replace(tscUsageSyntaxRegex, newAdditionToSyntax);
}
exports.manipulate = manipulate;
function detectState(line) {
const clearLine = stripAnsi(line);
const compilationStarted = compilationStartedRegex.test(clearLine);
const compilationError = compilationCompleteWithErrorRegex.test(clearLine) ||
typescriptErrorRegex.test(clearLine) ||
typescriptPrettyErrorRegex.test(clearLine);
const compilationComplete = compilationCompleteRegex.test(clearLine);
const fileEmittedExec = typescriptEmittedFileRegex.exec(clearLine);
const fileEmitted = fileEmittedExec !== null ? fileEmittedExec[2] : null; // if the regex is not null it will return an array with 3 elements
return {
compilationStarted,
compilationError,
compilationComplete,
fileEmitted,
};
}
exports.detectState = detectState;

View File

@ -0,0 +1,280 @@
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_cleanup_1 = __importStar(require("node-cleanup"));
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const runner_1 = require("./runner");
const args_manager_1 = require("./args-manager");
const debounce_1 = require("./debounce");
const stdout_manipulator_1 = require("./stdout-manipulator");
const readline_1 = require("readline");
let firstTime = true;
let firstSuccessKiller = null;
let successKiller = null;
let failureKiller = null;
let emitKiller = null;
let compilationStartedKiller = null;
let compilationCompleteKiller = null;
const { onFirstSuccessCommand, onSuccessCommand, onFailureCommand, onEmitCommand, onEmitDebounceMs, onCompilationStarted, onCompilationComplete, maxNodeMem, noColors, noClear, requestedToListEmittedFiles, signalEmittedFiles, silent, compiler, args, } = (0, args_manager_1.extractArgs)(process.argv);
let runningKillProcessesPromise = null;
function killProcesses(currentCompilationId, killAll) {
if (runningKillProcessesPromise) {
return runningKillProcessesPromise.then(() => currentCompilationId);
}
const promisesToWaitFor = [];
if (killAll && firstSuccessKiller) {
promisesToWaitFor.push(firstSuccessKiller());
firstSuccessKiller = null;
}
if (successKiller) {
promisesToWaitFor.push(successKiller());
successKiller = null;
}
if (failureKiller) {
promisesToWaitFor.push(failureKiller());
failureKiller = null;
}
if (compilationStartedKiller) {
promisesToWaitFor.push(compilationStartedKiller());
compilationStartedKiller = null;
}
if (compilationCompleteKiller) {
promisesToWaitFor.push(compilationCompleteKiller());
compilationCompleteKiller = null;
}
runningKillProcessesPromise = Promise.all(promisesToWaitFor).then(() => {
runningKillProcessesPromise = null;
return currentCompilationId;
});
return runningKillProcessesPromise;
}
let runningKillEmitProcessesPromise = null;
// The same as `killProcesses`, but we separate it to avoid canceling each other
function killEmitProcesses(currentEmitId) {
if (runningKillEmitProcessesPromise) {
return runningKillEmitProcessesPromise.then(() => currentEmitId);
}
let emitKilled = Promise.resolve();
if (emitKiller) {
emitKilled = emitKiller();
emitKiller = null;
}
runningKillEmitProcessesPromise = emitKilled.then(() => {
runningKillEmitProcessesPromise = null;
return currentEmitId;
});
return runningKillEmitProcessesPromise;
}
function runOnCompilationStarted() {
if (onCompilationStarted) {
compilationStartedKiller = (0, runner_1.run)(onCompilationStarted);
}
}
function runOnCompilationComplete() {
if (onCompilationComplete) {
compilationCompleteKiller = (0, runner_1.run)(onCompilationComplete);
}
}
function runOnFailureCommand() {
if (onFailureCommand) {
failureKiller = (0, runner_1.run)(onFailureCommand);
}
}
function runOnFirstSuccessCommand() {
if (onFirstSuccessCommand) {
firstSuccessKiller = (0, runner_1.run)(onFirstSuccessCommand);
}
}
function runOnSuccessCommand() {
if (onSuccessCommand) {
successKiller = (0, runner_1.run)(onSuccessCommand);
}
}
const debouncedEmit = onEmitCommand
? (0, debounce_1.debounce)(() => { emitKiller = (0, runner_1.run)(onEmitCommand); }, onEmitDebounceMs)
: undefined;
function runOnEmitCommand() {
debouncedEmit === null || debouncedEmit === void 0 ? void 0 : debouncedEmit();
}
function getTscPath() {
let tscBin;
try {
return require.resolve(compiler);
}
catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
console.error(e.message);
process.exit(9);
}
throw e;
}
}
function spawnTsc({ maxNodeMem, requestedToListEmittedFiles, signalEmittedFiles }, args) {
const tscBin = getTscPath();
const nodeArgs = [
...((maxNodeMem) ? [`--max_old_space_size=${maxNodeMem}`] : []),
tscBin,
...args
];
return (0, cross_spawn_1.default)('node', nodeArgs);
}
function echoExit(code, signal) {
if (signal !== null) {
process.kill(process.pid, signal);
}
}
let compilationErrorSinceStart = false;
const tscProcess = spawnTsc({ maxNodeMem, requestedToListEmittedFiles, signalEmittedFiles }, args);
if (!tscProcess.stdout) {
throw new Error('Unable to read Typescript stdout');
}
if (!tscProcess.stderr) {
throw new Error('Unable to read Typescript stderr');
}
tscProcess.on('exit', echoExit);
tscProcess.stderr.pipe(process.stderr);
const rl = (0, readline_1.createInterface)({ input: tscProcess.stdout });
let compilationId = 0;
let emitId = 0;
function triggerOnEmit() {
if (onEmitCommand) {
killEmitProcesses(++emitId).then((previousEmitId) => previousEmitId === emitId && runOnEmitCommand());
}
}
rl.on('line', function (input) {
if (noClear) {
input = (0, stdout_manipulator_1.deleteClear)(input);
}
const line = (0, stdout_manipulator_1.manipulate)(input);
if (!silent) {
(0, stdout_manipulator_1.print)(line, { noColors, noClear, signalEmittedFiles, requestedToListEmittedFiles });
}
const state = (0, stdout_manipulator_1.detectState)(line);
const compilationStarted = state.compilationStarted;
const compilationError = state.compilationError;
const compilationComplete = state.compilationComplete;
compilationErrorSinceStart =
(!compilationStarted && compilationErrorSinceStart) || compilationError;
if (state.fileEmitted !== null) {
Signal.emitFile(state.fileEmitted);
triggerOnEmit();
}
if (compilationStarted) {
compilationId++;
killProcesses(compilationId, false).then((previousCompilationId) => {
if (previousCompilationId !== compilationId) {
return;
}
runOnCompilationStarted();
Signal.emitStarted();
});
}
if (compilationComplete) {
compilationId++;
killProcesses(compilationId, false).then((previousCompilationId) => {
if (previousCompilationId !== compilationId) {
return;
}
runOnCompilationComplete();
if (compilationErrorSinceStart) {
Signal.emitFail();
runOnFailureCommand();
}
else {
if (firstTime) {
firstTime = false;
Signal.emitFirstSuccess();
runOnFirstSuccessCommand();
triggerOnEmit();
}
Signal.emitSuccess();
runOnSuccessCommand();
}
});
}
});
if (typeof process.on === 'function') {
process.on('message', (msg) => {
switch (msg) {
case 'run-on-compilation-started-command':
if (compilationStartedKiller) {
compilationStartedKiller().then(runOnCompilationStarted);
}
break;
case 'run-on-compilation-complete-command':
if (compilationCompleteKiller) {
compilationCompleteKiller().then(runOnCompilationComplete);
}
break;
case 'run-on-first-success-command':
if (firstSuccessKiller) {
firstSuccessKiller().then(runOnFirstSuccessCommand);
}
break;
case 'run-on-failure-command':
if (failureKiller) {
failureKiller().then(runOnFailureCommand);
}
break;
case 'run-on-success-command':
if (successKiller) {
successKiller().then(runOnSuccessCommand);
}
break;
case 'run-on-emit-command':
if (emitKiller) {
emitKiller().then(runOnEmitCommand);
}
break;
default:
console.log('Unknown message', msg);
}
});
}
const sendSignal = (msg) => {
if (process.send) {
process.send(msg);
}
};
const Signal = {
emitStarted: () => sendSignal('started'),
emitFirstSuccess: () => sendSignal('first_success'),
emitSuccess: () => sendSignal('success'),
emitFail: () => sendSignal('compile_errors'),
emitFile: (path) => sendSignal(`file_emitted:${path}`),
};
(0, node_cleanup_1.default)((_exitCode, signal) => {
if (signal) {
tscProcess.kill(signal);
}
killProcesses(0, true).then(() => process.exit());
// don't call cleanup handler again
(0, node_cleanup_1.uninstall)();
return false;
});

View File

@ -0,0 +1,3 @@
#!/usr/bin/env node
require('./dist/lib/tsc-watch');

View File

@ -0,0 +1,70 @@
{
"name": "tsc-watch",
"version": "6.2.0",
"description": "The TypeScript compiler with onSuccess command",
"scripts": {
"clean": "rimraf dist && rimraf tmp",
"prepublishOnly": "npm run build && crlf --set=LF index.js client.js dist/**/*",
"test": "npm run build && jest --verbose --runInBand",
"build": "npm run clean && npm run build-lib && npm run build-client",
"build-lib": "tsc -p tsconfig.json",
"build-client": "tsc -p tsconfig-client.json",
"watch-lib": "tsc -p tsconfig.json --watch"
},
"bin": {
"tsc-watch": "dist/lib/tsc-watch.js"
},
"files": [
"dist/**/*",
"index.js",
"client.js",
"client.d.ts"
],
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/gilamran/tsc-watch.git"
},
"keywords": [
"TypeScript"
],
"main": "./dist/client/index.js",
"types": "./dist/client/index.d.ts",
"engines": {
"node": ">=12.12.0"
},
"author": "Gil Amran",
"license": "MIT",
"bugs": {
"url": "https://github.com/gilamran/tsc-watch/issues"
},
"homepage": "https://github.com/gilamran/tsc-watch#readme",
"prettier": {
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 100
},
"dependencies": {
"cross-spawn": "^7.0.3",
"node-cleanup": "^2.1.2",
"ps-tree": "^1.2.0",
"string-argv": "^0.3.1"
},
"peerDependencies": {
"typescript": "*"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.2",
"@types/jest": "^29.5.1",
"@types/node": "^12.12.0",
"@types/node-cleanup": "^2.1.2",
"@types/ps-tree": "^1.1.2",
"crlf": "^1.1.1",
"fs-extra": "^11.1.1",
"jest": "^29.5.0",
"rimraf": "^5.0.0",
"ts-jest": "^29.1.0",
"typescript": "^4.6.3"
}
}