201 lines
5.8 KiB
TypeScript
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();
|
|
});
|
|
}
|
|
}
|