@git.zone/tswatch 2.3.12 → 3.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/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +4 -1
- package/dist_ts/index.js +5 -2
- package/dist_ts/interfaces/index.d.ts +1 -1
- package/dist_ts/interfaces/index.js +2 -2
- package/dist_ts/interfaces/interfaces.config.d.ts +58 -0
- package/dist_ts/interfaces/interfaces.config.js +2 -0
- package/dist_ts/tswatch.classes.confighandler.d.ts +30 -0
- package/dist_ts/tswatch.classes.confighandler.js +172 -0
- package/dist_ts/tswatch.classes.tswatch.d.ts +28 -3
- package/dist_ts/tswatch.classes.tswatch.js +135 -165
- package/dist_ts/tswatch.classes.watcher.d.ts +31 -3
- package/dist_ts/tswatch.classes.watcher.js +105 -25
- package/dist_ts/tswatch.cli.js +39 -28
- package/dist_ts/tswatch.init.d.ts +25 -0
- package/dist_ts/tswatch.init.js +168 -0
- package/dist_ts/tswatch.plugins.d.ts +3 -1
- package/dist_ts/tswatch.plugins.js +7 -5
- package/npmextra.json +12 -6
- package/package.json +22 -16
- package/readme.hints.md +88 -46
- package/readme.md +284 -149
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +6 -2
- package/ts/interfaces/index.ts +1 -1
- package/ts/interfaces/interfaces.config.ts +61 -0
- package/ts/tswatch.classes.confighandler.ts +185 -0
- package/ts/tswatch.classes.tswatch.ts +161 -197
- package/ts/tswatch.classes.watcher.ts +134 -23
- package/ts/tswatch.cli.ts +37 -31
- package/ts/tswatch.init.ts +199 -0
- package/ts/tswatch.plugins.ts +7 -3
- package/dist_ts/interfaces/interfaces.watchmodes.d.ts +0 -1
- package/dist_ts/interfaces/interfaces.watchmodes.js +0 -2
- package/ts/interfaces/interfaces.watchmodes.ts +0 -1
|
@@ -2,184 +2,154 @@ import * as plugins from './tswatch.plugins.js';
|
|
|
2
2
|
import * as paths from './tswatch.paths.js';
|
|
3
3
|
import * as interfaces from './interfaces/index.js';
|
|
4
4
|
import { Watcher } from './tswatch.classes.watcher.js';
|
|
5
|
+
import { ConfigHandler } from './tswatch.classes.confighandler.js';
|
|
5
6
|
import { logger } from './tswatch.logging.js';
|
|
6
|
-
// Create smartfs instance for directory operations
|
|
7
|
-
const smartfs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
8
7
|
/**
|
|
9
|
-
*
|
|
8
|
+
* TsWatch - Config-driven file watcher
|
|
9
|
+
*
|
|
10
|
+
* Reads configuration from npmextra.json under the key '@git.zone/tswatch'
|
|
11
|
+
* and sets up watchers, bundles, and dev server accordingly.
|
|
10
12
|
*/
|
|
11
|
-
const listFolders = async (dirPath) => {
|
|
12
|
-
const entries = await smartfs.directory(dirPath).list();
|
|
13
|
-
return entries
|
|
14
|
-
.filter((entry) => entry.isDirectory)
|
|
15
|
-
.map((entry) => entry.name);
|
|
16
|
-
};
|
|
17
13
|
export class TsWatch {
|
|
18
|
-
constructor(
|
|
14
|
+
constructor(configArg) {
|
|
19
15
|
this.watcherMap = new plugins.lik.ObjectMap();
|
|
20
|
-
this.
|
|
16
|
+
this.typedserver = null;
|
|
17
|
+
this.tsbundle = new plugins.tsbundle.TsBundle();
|
|
18
|
+
this.htmlHandler = new plugins.tsbundle.HtmlHandler();
|
|
19
|
+
this.assetsHandler = new plugins.tsbundle.AssetsHandler();
|
|
20
|
+
this.config = configArg;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create TsWatch from npmextra.json configuration
|
|
24
|
+
*/
|
|
25
|
+
static fromConfig(cwdArg) {
|
|
26
|
+
const configHandler = new ConfigHandler(cwdArg);
|
|
27
|
+
const config = configHandler.loadConfig();
|
|
28
|
+
if (!config) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return new TsWatch(config);
|
|
21
32
|
}
|
|
22
33
|
/**
|
|
23
34
|
* starts the TsWatch instance
|
|
24
35
|
*/
|
|
25
36
|
async start() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
case 'test':
|
|
31
|
-
/**
|
|
32
|
-
* this strategy runs test whenever there is a change in the ts directory
|
|
33
|
-
*/
|
|
34
|
-
this.watcherMap.add(new Watcher({
|
|
35
|
-
filePathToWatch: paths.cwd,
|
|
36
|
-
commandToExecute: 'npm run test2',
|
|
37
|
-
timeout: null,
|
|
38
|
-
}));
|
|
39
|
-
break;
|
|
40
|
-
case 'node':
|
|
41
|
-
this.watcherMap.add(new Watcher({
|
|
42
|
-
filePathToWatch: paths.cwd,
|
|
43
|
-
commandToExecute: 'npm run test',
|
|
44
|
-
timeout: null,
|
|
45
|
-
}));
|
|
46
|
-
break;
|
|
47
|
-
case 'element':
|
|
48
|
-
await (async () => {
|
|
49
|
-
/**
|
|
50
|
-
* this strategy runs a standard server and bundles the ts files to a dist_watch directory
|
|
51
|
-
*/
|
|
52
|
-
// lets create a standard server
|
|
53
|
-
logger.log('info', 'bundling TypeScript files to "dist_watch" Note: This is for development only!');
|
|
54
|
-
this.typedserver = new plugins.typedserver.TypedServer({
|
|
55
|
-
cors: true,
|
|
56
|
-
injectReload: true,
|
|
57
|
-
serveDir: plugins.path.join(paths.cwd, './dist_watch/'),
|
|
58
|
-
port: 3002,
|
|
59
|
-
compression: true,
|
|
60
|
-
spaFallback: true,
|
|
61
|
-
securityHeaders: {
|
|
62
|
-
crossOriginOpenerPolicy: 'same-origin',
|
|
63
|
-
crossOriginEmbedderPolicy: 'require-corp',
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
const bundleAndReloadElement = async () => {
|
|
67
|
-
await tsbundle.build(paths.cwd, './html/index.ts', './dist_watch/bundle.js', {
|
|
68
|
-
bundler: 'esbuild',
|
|
69
|
-
});
|
|
70
|
-
await this.typedserver.reload();
|
|
71
|
-
};
|
|
72
|
-
this.watcherMap.add(new Watcher({
|
|
73
|
-
filePathToWatch: plugins.path.join(paths.cwd, './ts_web/'),
|
|
74
|
-
functionToCall: async () => {
|
|
75
|
-
await bundleAndReloadElement();
|
|
76
|
-
},
|
|
77
|
-
timeout: null,
|
|
78
|
-
}));
|
|
79
|
-
// lets get the other ts folders
|
|
80
|
-
let tsfolders = await listFolders(paths.cwd);
|
|
81
|
-
tsfolders = tsfolders.filter((itemArg) => itemArg.startsWith('ts') && itemArg !== 'ts_web');
|
|
82
|
-
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
83
|
-
executor: 'bash',
|
|
84
|
-
});
|
|
85
|
-
for (const tsfolder of tsfolders) {
|
|
86
|
-
logger.log('info', `creating watcher for folder ${tsfolder}`);
|
|
87
|
-
this.watcherMap.add(new Watcher({
|
|
88
|
-
filePathToWatch: plugins.path.join(paths.cwd, `./${tsfolder}/`),
|
|
89
|
-
functionToCall: async () => {
|
|
90
|
-
logger.log('info', `building ${tsfolder}`);
|
|
91
|
-
await smartshellInstance.exec(`(cd ${paths.cwd} && npm run build)`);
|
|
92
|
-
await bundleAndReloadElement();
|
|
93
|
-
},
|
|
94
|
-
timeout: null,
|
|
95
|
-
}));
|
|
96
|
-
}
|
|
97
|
-
this.watcherMap.add(new Watcher({
|
|
98
|
-
filePathToWatch: plugins.path.join(paths.cwd, './html/'),
|
|
99
|
-
functionToCall: async () => {
|
|
100
|
-
await htmlHandler.processHtml({
|
|
101
|
-
from: plugins.path.join(paths.cwd, './html/index.html'),
|
|
102
|
-
to: plugins.path.join(paths.cwd, './dist_watch/index.html'),
|
|
103
|
-
minify: false,
|
|
104
|
-
});
|
|
105
|
-
await bundleAndReloadElement();
|
|
106
|
-
},
|
|
107
|
-
timeout: null,
|
|
108
|
-
}));
|
|
109
|
-
})();
|
|
110
|
-
break;
|
|
111
|
-
case 'website':
|
|
112
|
-
await (async () => {
|
|
113
|
-
const websiteExecution = new plugins.smartshell.SmartExecution('npm run startTs');
|
|
114
|
-
const bundleAndReloadWebsite = async () => {
|
|
115
|
-
await tsbundle.build(paths.cwd, './ts_web/index.ts', './dist_serve/bundle.js', {
|
|
116
|
-
bundler: 'esbuild',
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
let tsfolders = await listFolders(paths.cwd);
|
|
120
|
-
tsfolders = tsfolders.filter((itemArg) => itemArg.startsWith('ts') && itemArg !== 'ts_web');
|
|
121
|
-
for (const tsfolder of tsfolders) {
|
|
122
|
-
this.watcherMap.add(new Watcher({
|
|
123
|
-
filePathToWatch: plugins.path.join(paths.cwd, `./${tsfolder}/`),
|
|
124
|
-
functionToCall: async () => {
|
|
125
|
-
await websiteExecution.restart();
|
|
126
|
-
await bundleAndReloadWebsite();
|
|
127
|
-
},
|
|
128
|
-
timeout: null,
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
this.watcherMap.add(new Watcher({
|
|
132
|
-
filePathToWatch: plugins.path.join(paths.cwd, './ts_web/'),
|
|
133
|
-
functionToCall: async () => {
|
|
134
|
-
await bundleAndReloadWebsite();
|
|
135
|
-
},
|
|
136
|
-
timeout: null,
|
|
137
|
-
}));
|
|
138
|
-
this.watcherMap.add(new Watcher({
|
|
139
|
-
filePathToWatch: plugins.path.join(paths.cwd, './html/'),
|
|
140
|
-
functionToCall: async () => {
|
|
141
|
-
await htmlHandler.processHtml({
|
|
142
|
-
from: plugins.path.join(paths.cwd, './html/index.html'),
|
|
143
|
-
to: plugins.path.join(paths.cwd, './dist_serve/index.html'),
|
|
144
|
-
minify: false,
|
|
145
|
-
});
|
|
146
|
-
await bundleAndReloadWebsite();
|
|
147
|
-
},
|
|
148
|
-
timeout: null,
|
|
149
|
-
}));
|
|
150
|
-
this.watcherMap.add(new Watcher({
|
|
151
|
-
filePathToWatch: plugins.path.join(paths.cwd, './assets/'),
|
|
152
|
-
functionToCall: async () => {
|
|
153
|
-
await assetsHandler.processAssets();
|
|
154
|
-
await bundleAndReloadWebsite();
|
|
155
|
-
},
|
|
156
|
-
timeout: null,
|
|
157
|
-
}));
|
|
158
|
-
})();
|
|
159
|
-
break;
|
|
160
|
-
case 'service':
|
|
161
|
-
this.watcherMap.add(new Watcher({
|
|
162
|
-
filePathToWatch: plugins.path.join(paths.cwd, './ts/'),
|
|
163
|
-
commandToExecute: 'npm run startTs',
|
|
164
|
-
timeout: null,
|
|
165
|
-
}));
|
|
166
|
-
break;
|
|
167
|
-
case 'echo':
|
|
168
|
-
const tsWatchInstanceEchoSomething = new Watcher({
|
|
169
|
-
filePathToWatch: plugins.path.join(paths.cwd, './ts'),
|
|
170
|
-
commandToExecute: 'npm -v',
|
|
171
|
-
timeout: null,
|
|
172
|
-
});
|
|
173
|
-
this.watcherMap.add(tsWatchInstanceEchoSomething);
|
|
174
|
-
break;
|
|
175
|
-
default:
|
|
176
|
-
break;
|
|
37
|
+
logger.log('info', 'Starting tswatch with config-driven mode');
|
|
38
|
+
// Start server if configured
|
|
39
|
+
if (this.config.server?.enabled) {
|
|
40
|
+
await this.startServer();
|
|
177
41
|
}
|
|
178
|
-
|
|
42
|
+
// Setup bundles and their watchers
|
|
43
|
+
if (this.config.bundles && this.config.bundles.length > 0) {
|
|
44
|
+
await this.setupBundles();
|
|
45
|
+
}
|
|
46
|
+
// Setup watchers from config
|
|
47
|
+
if (this.config.watchers && this.config.watchers.length > 0) {
|
|
48
|
+
await this.setupWatchers();
|
|
49
|
+
}
|
|
50
|
+
// Start all watchers
|
|
51
|
+
await this.watcherMap.forEach(async (watcher) => {
|
|
179
52
|
await watcher.start();
|
|
180
53
|
});
|
|
54
|
+
// Start server after watchers are ready
|
|
181
55
|
if (this.typedserver) {
|
|
182
56
|
await this.typedserver.start();
|
|
57
|
+
logger.log('ok', `Dev server started on port ${this.config.server?.port || 3002}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Start the development server
|
|
62
|
+
*/
|
|
63
|
+
async startServer() {
|
|
64
|
+
const serverConfig = this.config.server;
|
|
65
|
+
const port = serverConfig.port || 3002;
|
|
66
|
+
const serveDir = serverConfig.serveDir || './dist_watch/';
|
|
67
|
+
logger.log('info', `Setting up dev server on port ${port}, serving ${serveDir}`);
|
|
68
|
+
this.typedserver = new plugins.typedserver.TypedServer({
|
|
69
|
+
cors: true,
|
|
70
|
+
injectReload: serverConfig.liveReload !== false,
|
|
71
|
+
serveDir: plugins.path.join(paths.cwd, serveDir),
|
|
72
|
+
port: port,
|
|
73
|
+
compression: true,
|
|
74
|
+
spaFallback: true,
|
|
75
|
+
securityHeaders: {
|
|
76
|
+
crossOriginOpenerPolicy: 'same-origin',
|
|
77
|
+
crossOriginEmbedderPolicy: 'require-corp',
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Setup bundle watchers
|
|
83
|
+
*/
|
|
84
|
+
async setupBundles() {
|
|
85
|
+
for (const bundleConfig of this.config.bundles) {
|
|
86
|
+
const name = bundleConfig.name || `bundle-${bundleConfig.from}`;
|
|
87
|
+
logger.log('info', `Setting up bundle: ${name}`);
|
|
88
|
+
// Determine what patterns to watch
|
|
89
|
+
const watchPatterns = bundleConfig.watchPatterns || [
|
|
90
|
+
plugins.path.dirname(bundleConfig.from) + '/**/*',
|
|
91
|
+
];
|
|
92
|
+
// Create the bundle function
|
|
93
|
+
const bundleFunction = async () => {
|
|
94
|
+
logger.log('info', `[${name}] bundling...`);
|
|
95
|
+
// Determine bundle type based on file extension
|
|
96
|
+
const fromPath = bundleConfig.from;
|
|
97
|
+
const toPath = bundleConfig.to;
|
|
98
|
+
if (fromPath.endsWith('.html')) {
|
|
99
|
+
// HTML processing
|
|
100
|
+
await this.htmlHandler.processHtml({
|
|
101
|
+
from: plugins.path.join(paths.cwd, fromPath),
|
|
102
|
+
to: plugins.path.join(paths.cwd, toPath),
|
|
103
|
+
minify: false,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (fromPath.endsWith('/') || !fromPath.includes('.')) {
|
|
107
|
+
// Assets directory copy
|
|
108
|
+
await this.assetsHandler.processAssets();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// TypeScript bundling
|
|
112
|
+
await this.tsbundle.build(paths.cwd, fromPath, toPath, {
|
|
113
|
+
bundler: 'esbuild',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
logger.log('ok', `[${name}] bundle complete`);
|
|
117
|
+
// Trigger reload if configured and server is running
|
|
118
|
+
if (bundleConfig.triggerReload !== false && this.typedserver) {
|
|
119
|
+
await this.typedserver.reload();
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
// Run initial bundle
|
|
123
|
+
await bundleFunction();
|
|
124
|
+
// Create watcher for this bundle
|
|
125
|
+
this.watcherMap.add(new Watcher({
|
|
126
|
+
name: name,
|
|
127
|
+
filePathToWatch: watchPatterns.map((p) => plugins.path.join(paths.cwd, p)),
|
|
128
|
+
functionToCall: bundleFunction,
|
|
129
|
+
runOnStart: false, // Already ran above
|
|
130
|
+
debounce: 300,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Setup watchers from config
|
|
136
|
+
*/
|
|
137
|
+
async setupWatchers() {
|
|
138
|
+
for (const watcherConfig of this.config.watchers) {
|
|
139
|
+
logger.log('info', `Setting up watcher: ${watcherConfig.name}`);
|
|
140
|
+
// Convert watch paths to absolute
|
|
141
|
+
const watchPaths = Array.isArray(watcherConfig.watch)
|
|
142
|
+
? watcherConfig.watch
|
|
143
|
+
: [watcherConfig.watch];
|
|
144
|
+
const absolutePaths = watchPaths.map((p) => plugins.path.join(paths.cwd, p));
|
|
145
|
+
this.watcherMap.add(new Watcher({
|
|
146
|
+
name: watcherConfig.name,
|
|
147
|
+
filePathToWatch: absolutePaths,
|
|
148
|
+
commandToExecute: watcherConfig.command,
|
|
149
|
+
restart: watcherConfig.restart ?? true,
|
|
150
|
+
debounce: watcherConfig.debounce ?? 300,
|
|
151
|
+
runOnStart: watcherConfig.runOnStart ?? true,
|
|
152
|
+
}));
|
|
183
153
|
}
|
|
184
154
|
}
|
|
185
155
|
/**
|
|
@@ -189,9 +159,9 @@ export class TsWatch {
|
|
|
189
159
|
if (this.typedserver) {
|
|
190
160
|
await this.typedserver.stop();
|
|
191
161
|
}
|
|
192
|
-
this.watcherMap.forEach(async (watcher) => {
|
|
162
|
+
await this.watcherMap.forEach(async (watcher) => {
|
|
193
163
|
await watcher.stop();
|
|
194
164
|
});
|
|
195
165
|
}
|
|
196
166
|
}
|
|
197
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
167
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN3YXRjaC5jbGFzc2VzLnRzd2F0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c3dhdGNoLmNsYXNzZXMudHN3YXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHNCQUFzQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxLQUFLLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxLQUFLLFVBQVUsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUU5Qzs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxPQUFPO0lBU2xCLFlBQVksU0FBb0M7UUFQekMsZUFBVSxHQUFHLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQVcsQ0FBQztRQUNsRCxnQkFBVyxHQUEyQyxJQUFJLENBQUM7UUFFMUQsYUFBUSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUMzQyxnQkFBVyxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNqRCxrQkFBYSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUczRCxJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUMxQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQWU7UUFDdEMsTUFBTSxhQUFhLEdBQUcsSUFBSSxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDaEQsTUFBTSxNQUFNLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBRTFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZ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
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
import * as interfaces from './interfaces/index.js';
|
|
1
2
|
export interface IWatcherConstructorOptions {
|
|
2
|
-
|
|
3
|
+
/** Name for this watcher (used in logging) */
|
|
4
|
+
name?: string;
|
|
5
|
+
/** Path(s) to watch - can be a single path or array */
|
|
6
|
+
filePathToWatch: string | string[];
|
|
7
|
+
/** Shell command to execute on changes */
|
|
3
8
|
commandToExecute?: string;
|
|
9
|
+
/** Function to call on changes */
|
|
4
10
|
functionToCall?: () => Promise<any>;
|
|
11
|
+
/** Timeout for the watcher */
|
|
5
12
|
timeout?: number;
|
|
13
|
+
/** If true, kill previous process before restarting (default: true) */
|
|
14
|
+
restart?: boolean;
|
|
15
|
+
/** Debounce delay in ms (default: 300) */
|
|
16
|
+
debounce?: number;
|
|
17
|
+
/** If true, run the command immediately on start (default: true) */
|
|
18
|
+
runOnStart?: boolean;
|
|
6
19
|
}
|
|
7
20
|
/**
|
|
8
21
|
* A watcher keeps track of one child execution
|
|
@@ -15,15 +28,30 @@ export declare class Watcher {
|
|
|
15
28
|
private currentExecution;
|
|
16
29
|
private smartwatchInstance;
|
|
17
30
|
private options;
|
|
31
|
+
private debounceTimer;
|
|
32
|
+
private isExecuting;
|
|
33
|
+
private pendingExecution;
|
|
18
34
|
constructor(optionsArg: IWatcherConstructorOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Create a Watcher from config
|
|
37
|
+
*/
|
|
38
|
+
static fromConfig(config: interfaces.IWatcherConfig): Watcher;
|
|
39
|
+
/**
|
|
40
|
+
* Get the watcher name for logging
|
|
41
|
+
*/
|
|
42
|
+
private getName;
|
|
19
43
|
/**
|
|
20
44
|
* start the file
|
|
21
45
|
*/
|
|
22
46
|
start(): Promise<void>;
|
|
23
47
|
/**
|
|
24
|
-
*
|
|
48
|
+
* Handle file change with debouncing
|
|
49
|
+
*/
|
|
50
|
+
private handleChange;
|
|
51
|
+
/**
|
|
52
|
+
* Execute the command or function
|
|
25
53
|
*/
|
|
26
|
-
private
|
|
54
|
+
private executeCommand;
|
|
27
55
|
/**
|
|
28
56
|
* this method sets up a clean exit strategy
|
|
29
57
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as plugins from './tswatch.plugins.js';
|
|
2
|
+
import * as interfaces from './interfaces/index.js';
|
|
2
3
|
import { logger } from './tswatch.logging.js';
|
|
3
4
|
/**
|
|
4
5
|
* A watcher keeps track of one child execution
|
|
@@ -12,50 +13,126 @@ export class Watcher {
|
|
|
12
13
|
executor: 'bash',
|
|
13
14
|
});
|
|
14
15
|
this.smartwatchInstance = new plugins.smartwatch.Smartwatch([]);
|
|
15
|
-
this.
|
|
16
|
+
this.debounceTimer = null;
|
|
17
|
+
this.isExecuting = false;
|
|
18
|
+
this.pendingExecution = false;
|
|
19
|
+
this.options = {
|
|
20
|
+
restart: true,
|
|
21
|
+
debounce: 300,
|
|
22
|
+
runOnStart: true,
|
|
23
|
+
...optionsArg,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a Watcher from config
|
|
28
|
+
*/
|
|
29
|
+
static fromConfig(config) {
|
|
30
|
+
const watchPaths = Array.isArray(config.watch) ? config.watch : [config.watch];
|
|
31
|
+
return new Watcher({
|
|
32
|
+
name: config.name,
|
|
33
|
+
filePathToWatch: watchPaths,
|
|
34
|
+
commandToExecute: config.command,
|
|
35
|
+
restart: config.restart ?? true,
|
|
36
|
+
debounce: config.debounce ?? 300,
|
|
37
|
+
runOnStart: config.runOnStart ?? true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the watcher name for logging
|
|
42
|
+
*/
|
|
43
|
+
getName() {
|
|
44
|
+
return this.options.name || 'unnamed';
|
|
16
45
|
}
|
|
17
46
|
/**
|
|
18
47
|
* start the file
|
|
19
48
|
*/
|
|
20
49
|
async start() {
|
|
21
|
-
|
|
50
|
+
const name = this.getName();
|
|
51
|
+
logger.log('info', `[${name}] starting watcher`);
|
|
22
52
|
await this.setupCleanup();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
// Convert paths to glob patterns
|
|
54
|
+
const paths = Array.isArray(this.options.filePathToWatch)
|
|
55
|
+
? this.options.filePathToWatch
|
|
56
|
+
: [this.options.filePathToWatch];
|
|
57
|
+
const watchPatterns = paths.map((p) => {
|
|
58
|
+
// Convert directory path to glob pattern for smartwatch
|
|
59
|
+
if (p.endsWith('/')) {
|
|
60
|
+
return `${p}**/*`;
|
|
61
|
+
}
|
|
62
|
+
// If it's already a glob pattern, use as-is
|
|
63
|
+
if (p.includes('*')) {
|
|
64
|
+
return p;
|
|
65
|
+
}
|
|
66
|
+
// Otherwise assume it's a directory
|
|
67
|
+
return `${p}/**/*`;
|
|
68
|
+
});
|
|
69
|
+
logger.log('info', `[${name}] watching patterns: ${watchPatterns.join(', ')}`);
|
|
70
|
+
this.smartwatchInstance.add(watchPatterns);
|
|
29
71
|
await this.smartwatchInstance.start();
|
|
30
72
|
const changeObservable = await this.smartwatchInstance.getObservableFor('change');
|
|
31
73
|
changeObservable.subscribe(() => {
|
|
32
|
-
this.
|
|
74
|
+
this.handleChange();
|
|
33
75
|
});
|
|
34
|
-
|
|
35
|
-
|
|
76
|
+
// Run on start if configured
|
|
77
|
+
if (this.options.runOnStart) {
|
|
78
|
+
await this.executeCommand();
|
|
79
|
+
}
|
|
80
|
+
logger.log('info', `[${name}] watcher started`);
|
|
36
81
|
}
|
|
37
82
|
/**
|
|
38
|
-
*
|
|
83
|
+
* Handle file change with debouncing
|
|
39
84
|
*/
|
|
40
|
-
|
|
85
|
+
handleChange() {
|
|
86
|
+
const name = this.getName();
|
|
87
|
+
// Clear existing debounce timer
|
|
88
|
+
if (this.debounceTimer) {
|
|
89
|
+
clearTimeout(this.debounceTimer);
|
|
90
|
+
}
|
|
91
|
+
// Set new debounce timer
|
|
92
|
+
this.debounceTimer = setTimeout(async () => {
|
|
93
|
+
this.debounceTimer = null;
|
|
94
|
+
// If currently executing and not in restart mode, mark pending
|
|
95
|
+
if (this.isExecuting && !this.options.restart) {
|
|
96
|
+
logger.log('info', `[${name}] change detected, queuing execution`);
|
|
97
|
+
this.pendingExecution = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
await this.executeCommand();
|
|
101
|
+
// If there was a pending execution, run it
|
|
102
|
+
if (this.pendingExecution) {
|
|
103
|
+
this.pendingExecution = false;
|
|
104
|
+
await this.executeCommand();
|
|
105
|
+
}
|
|
106
|
+
}, this.options.debounce);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Execute the command or function
|
|
110
|
+
*/
|
|
111
|
+
async executeCommand() {
|
|
112
|
+
const name = this.getName();
|
|
41
113
|
if (this.options.commandToExecute) {
|
|
42
|
-
if (this.currentExecution) {
|
|
43
|
-
logger.log('ok', `
|
|
114
|
+
if (this.currentExecution && this.options.restart) {
|
|
115
|
+
logger.log('ok', `[${name}] restarting: ${this.options.commandToExecute}`);
|
|
44
116
|
this.currentExecution.kill();
|
|
45
117
|
}
|
|
46
|
-
else {
|
|
47
|
-
logger.log('ok', `executing ${this.options.commandToExecute}
|
|
118
|
+
else if (!this.currentExecution) {
|
|
119
|
+
logger.log('ok', `[${name}] executing: ${this.options.commandToExecute}`);
|
|
48
120
|
}
|
|
121
|
+
this.isExecuting = true;
|
|
49
122
|
this.currentExecution = await this.smartshellInstance.execStreaming(this.options.commandToExecute);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
123
|
+
// Track when execution completes
|
|
124
|
+
this.currentExecution.childProcess.on('exit', () => {
|
|
125
|
+
this.isExecuting = false;
|
|
126
|
+
});
|
|
53
127
|
}
|
|
54
128
|
if (this.options.functionToCall) {
|
|
55
|
-
this.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
129
|
+
this.isExecuting = true;
|
|
130
|
+
try {
|
|
131
|
+
await this.options.functionToCall();
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
this.isExecuting = false;
|
|
135
|
+
}
|
|
59
136
|
}
|
|
60
137
|
}
|
|
61
138
|
/**
|
|
@@ -87,10 +164,13 @@ export class Watcher {
|
|
|
87
164
|
* stops the watcher
|
|
88
165
|
*/
|
|
89
166
|
async stop() {
|
|
167
|
+
if (this.debounceTimer) {
|
|
168
|
+
clearTimeout(this.debounceTimer);
|
|
169
|
+
}
|
|
90
170
|
await this.smartwatchInstance.stop();
|
|
91
171
|
if (this.currentExecution && !this.currentExecution.childProcess.killed) {
|
|
92
172
|
this.currentExecution.kill();
|
|
93
173
|
}
|
|
94
174
|
}
|
|
95
175
|
}
|
|
96
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
176
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN3YXRjaC5jbGFzc2VzLndhdGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c3dhdGNoLmNsYXNzZXMud2F0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHNCQUFzQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxVQUFVLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBcUI5Qzs7R0FFRztBQUNILE1BQU0sT0FBTyxPQUFPO0lBZWxCLFlBQVksVUFBc0M7UUFkbEQ7O1dBRUc7UUFDSyx1QkFBa0IsR0FBRyxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDO1lBQzdELFFBQVEsRUFBRSxNQUFNO1NBQ2pCLENBQUMsQ0FBQztRQUdLLHVCQUFrQixHQUFHLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFM0Qsa0JBQWEsR0FBMEIsSUFBSSxDQUFDO1FBQzVDLGdCQUFXLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLHFCQUFnQixHQUFHLEtBQUssQ0FBQztRQUcvQixJQUFJLENBQUMsT0FBTyxHQUFHO1lBQ2IsT0FBTyxFQUFFLElBQUk7WUFDYixRQUFRLEVBQUUsR0FBRztZQUNiLFVBQVUsRUFBRSxJQUFJO1lBQ2hCLEdBQUcsVUFBVTtTQUNkLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQWlDO1FBQ3hELE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMvRSxPQUFPLElBQUksT0FBTyxDQUFDO1lBQ2pCLElBQUksRUFBRSxNQUFN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
|