BREAKING CHANGE(tswatch): refactor tswatch to a config-driven design (load config from npmextra.json) and add interactive init wizard; change TsWatch public API and enhance Watcher behavior
This commit is contained in:
199
ts/tswatch.init.ts
Normal file
199
ts/tswatch.init.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import * as plugins from './tswatch.plugins.js';
|
||||
import * as paths from './tswatch.paths.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import { ConfigHandler } from './tswatch.classes.confighandler.js';
|
||||
import { logger } from './tswatch.logging.js';
|
||||
|
||||
const CONFIG_KEY = '@git.zone/tswatch';
|
||||
|
||||
/**
|
||||
* Interactive init wizard for creating tswatch configuration
|
||||
*/
|
||||
export class TswatchInit {
|
||||
private configHandler: ConfigHandler;
|
||||
private smartInteract: plugins.smartinteract.SmartInteract;
|
||||
|
||||
constructor() {
|
||||
this.configHandler = new ConfigHandler();
|
||||
this.smartInteract = new plugins.smartinteract.SmartInteract([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the interactive init wizard
|
||||
*/
|
||||
public async run(): Promise<interfaces.ITswatchConfig | null> {
|
||||
console.log('\n=== tswatch Configuration Wizard ===\n');
|
||||
|
||||
// Ask for template choice
|
||||
const templateAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'template',
|
||||
type: 'list',
|
||||
message: 'Select a configuration template:',
|
||||
default: 'npm',
|
||||
choices: [
|
||||
{ name: 'npm - Watch ts/ and test/, run npm test', value: 'npm' },
|
||||
{ name: 'test - Watch ts/ and test/, run npm run test2', value: 'test' },
|
||||
{ name: 'service - Watch ts/, restart npm run startTs', value: 'service' },
|
||||
{ name: 'element - Dev server + bundling for web components', value: 'element' },
|
||||
{ name: 'website - Full stack: backend + frontend + assets', value: 'website' },
|
||||
{ name: 'custom - Configure watchers manually', value: 'custom' },
|
||||
],
|
||||
});
|
||||
|
||||
const template = templateAnswer.value as string;
|
||||
|
||||
let config: interfaces.ITswatchConfig;
|
||||
|
||||
if (template === 'custom') {
|
||||
config = await this.runCustomWizard();
|
||||
} else {
|
||||
// Get preset config
|
||||
const preset = this.configHandler.getPreset(template);
|
||||
if (!preset) {
|
||||
console.error(`Unknown template: ${template}`);
|
||||
return null;
|
||||
}
|
||||
config = { ...preset, preset: template as interfaces.ITswatchConfig['preset'] };
|
||||
}
|
||||
|
||||
// Save to npmextra.json
|
||||
await this.saveConfig(config);
|
||||
|
||||
console.log('\nConfiguration saved to npmextra.json');
|
||||
console.log('Run "tswatch" to start watching.\n');
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run custom configuration wizard
|
||||
*/
|
||||
private async runCustomWizard(): Promise<interfaces.ITswatchConfig> {
|
||||
const config: interfaces.ITswatchConfig = {};
|
||||
|
||||
// Ask about server
|
||||
const serverAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'enableServer',
|
||||
type: 'confirm',
|
||||
message: 'Enable development server?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
if (serverAnswer.value) {
|
||||
const portAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'port',
|
||||
type: 'input',
|
||||
message: 'Server port:',
|
||||
default: '3002',
|
||||
});
|
||||
|
||||
const serveDirAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'serveDir',
|
||||
type: 'input',
|
||||
message: 'Directory to serve:',
|
||||
default: './dist_watch/',
|
||||
});
|
||||
|
||||
config.server = {
|
||||
enabled: true,
|
||||
port: parseInt(portAnswer.value as string, 10),
|
||||
serveDir: serveDirAnswer.value as string,
|
||||
liveReload: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Add watchers
|
||||
config.watchers = [];
|
||||
let addMore = true;
|
||||
|
||||
while (addMore) {
|
||||
console.log('\n--- Add a watcher ---');
|
||||
|
||||
const nameAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'name',
|
||||
type: 'input',
|
||||
message: 'Watcher name:',
|
||||
default: `watcher-${config.watchers.length + 1}`,
|
||||
});
|
||||
|
||||
const watchAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'watch',
|
||||
type: 'input',
|
||||
message: 'Glob pattern(s) to watch (comma-separated):',
|
||||
default: './ts/**/*',
|
||||
});
|
||||
|
||||
const commandAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'command',
|
||||
type: 'input',
|
||||
message: 'Command to execute:',
|
||||
default: 'npm run test',
|
||||
});
|
||||
|
||||
const restartAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'restart',
|
||||
type: 'confirm',
|
||||
message: 'Restart command on each change (vs queue)?',
|
||||
default: true,
|
||||
});
|
||||
|
||||
// Parse watch patterns
|
||||
const watchPatterns = (watchAnswer.value as string)
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.filter((p) => p.length > 0);
|
||||
|
||||
config.watchers.push({
|
||||
name: nameAnswer.value as string,
|
||||
watch: watchPatterns.length === 1 ? watchPatterns[0] : watchPatterns,
|
||||
command: commandAnswer.value as string,
|
||||
restart: restartAnswer.value as boolean,
|
||||
debounce: 300,
|
||||
runOnStart: true,
|
||||
});
|
||||
|
||||
const moreAnswer = await this.smartInteract.askQuestion({
|
||||
name: 'addMore',
|
||||
type: 'confirm',
|
||||
message: 'Add another watcher?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
addMore = moreAnswer.value as boolean;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save configuration to npmextra.json
|
||||
*/
|
||||
private async saveConfig(config: interfaces.ITswatchConfig): Promise<void> {
|
||||
const npmextraPath = plugins.path.join(paths.cwd, 'npmextra.json');
|
||||
|
||||
// Read existing npmextra.json if it exists
|
||||
let existingConfig: Record<string, any> = {};
|
||||
try {
|
||||
const smartfsInstance = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
||||
const content = await smartfsInstance.file(npmextraPath).encoding('utf8').read() as string;
|
||||
existingConfig = JSON.parse(content);
|
||||
} catch {
|
||||
// File doesn't exist or is invalid, start fresh
|
||||
}
|
||||
|
||||
// Update with new tswatch config
|
||||
existingConfig[CONFIG_KEY] = config;
|
||||
|
||||
// Write back
|
||||
const smartfsInstance = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
||||
await smartfsInstance.file(npmextraPath).encoding('utf8').write(JSON.stringify(existingConfig, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the init wizard
|
||||
*/
|
||||
export const runInit = async (): Promise<interfaces.ITswatchConfig | null> => {
|
||||
const init = new TswatchInit();
|
||||
return init.run();
|
||||
};
|
||||
Reference in New Issue
Block a user