Files
tswatch/ts/tswatch.classes.tswatch.ts

201 lines
5.8 KiB
TypeScript

import * as plugins from './tswatch.plugins.js';
import * as paths from './tswatch.paths.js';
import * as interfaces from './interfaces/index.js';
import { Watcher } from './tswatch.classes.watcher.js';
import { ConfigHandler } from './tswatch.classes.confighandler.js';
import { logger } from './tswatch.logging.js';
/**
* TsWatch - Config-driven file watcher
*
* Reads configuration from npmextra.json under the key '@git.zone/tswatch'
* and sets up watchers, bundles, and dev server accordingly.
*/
export class TsWatch {
public config: interfaces.ITswatchConfig;
public watcherMap = new plugins.lik.ObjectMap<Watcher>();
public typedserver: plugins.typedserver.TypedServer | null = null;
private tsbundle = new plugins.tsbundle.TsBundle();
private htmlHandler = new plugins.tsbundle.HtmlHandler();
private assetsHandler = new plugins.tsbundle.AssetsHandler();
constructor(configArg: interfaces.ITswatchConfig) {
this.config = configArg;
}
/**
* Create TsWatch from npmextra.json configuration
*/
public static fromConfig(cwdArg?: string): TsWatch | null {
const configHandler = new ConfigHandler(cwdArg);
const config = configHandler.loadConfig();
if (!config) {
return null;
}
return new TsWatch(config);
}
/**
* starts the TsWatch instance
*/
public async start() {
logger.log('info', 'Starting tswatch with config-driven mode');
// Start server if configured
if (this.config.server?.enabled) {
await this.startServer();
}
// Setup bundles and their watchers
if (this.config.bundles && this.config.bundles.length > 0) {
await this.setupBundles();
}
// Setup watchers from config
if (this.config.watchers && this.config.watchers.length > 0) {
await this.setupWatchers();
}
// Start all watchers
await this.watcherMap.forEach(async (watcher) => {
await watcher.start();
});
// Start server after watchers are ready
if (this.typedserver) {
await this.typedserver.start();
logger.log('ok', `Dev server started on port ${this.config.server?.port || 3002}`);
}
}
/**
* Start the development server
*/
private async startServer() {
const serverConfig = this.config.server!;
const port = serverConfig.port || 3002;
const serveDir = serverConfig.serveDir || './dist_watch/';
logger.log('info', `Setting up dev server on port ${port}, serving ${serveDir}`);
this.typedserver = new plugins.typedserver.TypedServer({
cors: true,
injectReload: serverConfig.liveReload !== false,
serveDir: plugins.path.join(paths.cwd, serveDir),
port: port,
compression: true,
spaFallback: true,
noCache: true,
securityHeaders: {
crossOriginOpenerPolicy: 'same-origin',
crossOriginEmbedderPolicy: 'require-corp',
},
});
}
/**
* Setup bundle watchers
*/
private async setupBundles() {
for (const bundleConfig of this.config.bundles!) {
const name = bundleConfig.name || `bundle-${bundleConfig.from}`;
logger.log('info', `Setting up bundle: ${name}`);
// Determine what patterns to watch
const watchPatterns = bundleConfig.watchPatterns || [
plugins.path.dirname(bundleConfig.from) + '/**/*',
];
// Create the bundle function
const bundleFunction = async () => {
logger.log('info', `[${name}] bundling...`);
// Determine bundle type based on file extension
const fromPath = bundleConfig.from;
const toPath = bundleConfig.to;
if (fromPath.endsWith('.html')) {
// HTML processing
await this.htmlHandler.processHtml({
from: plugins.path.join(paths.cwd, fromPath),
to: plugins.path.join(paths.cwd, toPath),
minify: false,
});
} else if (fromPath.endsWith('/') || !fromPath.includes('.')) {
// Assets directory copy
await this.assetsHandler.processAssets();
} else {
// TypeScript bundling
await this.tsbundle.build(paths.cwd, fromPath, toPath, {
bundler: 'esbuild',
});
}
logger.log('ok', `[${name}] bundle complete`);
// Trigger reload if configured and server is running
if (bundleConfig.triggerReload !== false && this.typedserver) {
await this.typedserver.reload();
}
};
// Run initial bundle
await bundleFunction();
// Create watcher for this bundle
this.watcherMap.add(
new Watcher({
name: name,
filePathToWatch: watchPatterns.map((p) => plugins.path.join(paths.cwd, p)),
functionToCall: bundleFunction,
runOnStart: false, // Already ran above
debounce: 300,
}),
);
}
}
/**
* Setup watchers from config
*/
private async setupWatchers() {
for (const watcherConfig of this.config.watchers!) {
logger.log('info', `Setting up watcher: ${watcherConfig.name}`);
// Convert watch paths to absolute
const watchPaths = Array.isArray(watcherConfig.watch)
? watcherConfig.watch
: [watcherConfig.watch];
const absolutePaths = watchPaths.map((p) => plugins.path.join(paths.cwd, p));
this.watcherMap.add(
new Watcher({
name: watcherConfig.name,
filePathToWatch: absolutePaths,
commandToExecute: watcherConfig.command,
restart: watcherConfig.restart ?? true,
debounce: watcherConfig.debounce ?? 300,
runOnStart: watcherConfig.runOnStart ?? true,
}),
);
}
}
/**
* stops the execution of any active Watchers
*/
public async stop() {
if (this.typedserver) {
await this.typedserver.stop();
}
await this.watcherMap.forEach(async (watcher) => {
await watcher.stop();
});
}
}