Files
tsbundle/ts/mod_custom/index.ts

204 lines
6.6 KiB
TypeScript

import * as plugins from './plugins.js';
import * as paths from '../paths.js';
import * as interfaces from '../interfaces/index.js';
import { TsBundle } from '../tsbundle.class.tsbundle.js';
import { HtmlHandler } from '../mod_html/index.js';
import { Base64TsOutput } from '../mod_output/index.js';
const TEMP_DIR = '.nogit/tsbundle-temp';
export class CustomBundleHandler {
private cwd: string;
private config: interfaces.ITsbundleConfig;
constructor(cwd: string = paths.cwd) {
this.cwd = cwd;
}
/**
* Load configuration from npmextra.json
*/
public async loadConfig(): Promise<boolean> {
const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
this.config = npmextraInstance.dataFor<interfaces.ITsbundleConfig>('@git.zone/tsbundle', {
bundles: [],
});
if (!this.config.bundles || this.config.bundles.length === 0) {
console.log('No bundle configuration found.');
console.log('Run `tsbundle init` to create one.');
return false;
}
console.log(`Found ${this.config.bundles.length} bundle configuration(s)`);
return true;
}
/**
* Process all configured bundles
*/
public async processAllBundles(): Promise<void> {
for (let i = 0; i < this.config.bundles.length; i++) {
const bundleConfig = this.config.bundles[i];
console.log(`\nProcessing bundle ${i + 1}/${this.config.bundles.length}: ${bundleConfig.from} -> ${bundleConfig.to}`);
await this.processSingleBundle(bundleConfig);
}
}
/**
* Process a single bundle configuration
*/
private async processSingleBundle(bundleConfig: interfaces.IBundleConfig): Promise<void> {
const outputMode = bundleConfig.outputMode || 'bundle';
const bundler = bundleConfig.bundler || 'esbuild';
// Determine temp output path
const tempDir = plugins.path.join(this.cwd, TEMP_DIR);
const tempBundlePath = plugins.path.join(tempDir, `bundle-${Date.now()}.js`);
// Ensure temp directory exists
await plugins.fs.directory(tempDir).create();
// Build the bundle to temp location
const tsbundle = new TsBundle();
await tsbundle.build(
this.cwd,
bundleConfig.from,
tempBundlePath,
{
bundler,
production: bundleConfig.production || false,
}
);
if (outputMode === 'base64ts') {
await this.handleBase64TsOutput(bundleConfig, tempBundlePath);
} else {
await this.handleBundleOutput(bundleConfig, tempBundlePath);
}
// Clean up temp file
const tempFileExists = await plugins.fs.file(tempBundlePath).exists();
if (tempFileExists) {
await plugins.fs.file(tempBundlePath).delete();
}
}
/**
* Handle base64ts output mode
*/
private async handleBase64TsOutput(
bundleConfig: interfaces.IBundleConfig,
tempBundlePath: string
): Promise<void> {
const base64Output = new Base64TsOutput(this.cwd);
// Add the bundle itself
const bundleContent = await plugins.fs.file(tempBundlePath).read();
base64Output.addFile('bundle.js', bundleContent);
// Add included files
if (bundleConfig.includeFiles && bundleConfig.includeFiles.length > 0) {
for (const pattern of bundleConfig.includeFiles) {
await base64Output.addFilesFromGlob(pattern);
}
}
// Write the TypeScript output
await base64Output.writeToFile(bundleConfig.to);
}
/**
* Handle standard bundle output mode
*/
private async handleBundleOutput(
bundleConfig: interfaces.IBundleConfig,
tempBundlePath: string
): Promise<void> {
// Move bundle to final destination
const toPath = plugins.smartpath.transform.toAbsolute(bundleConfig.to, this.cwd) as string;
const toDir = plugins.path.dirname(toPath);
await plugins.fs.directory(toDir).create();
const bundleContent = await plugins.fs.file(tempBundlePath).read();
await plugins.fs.file(toPath).write(bundleContent);
console.log(`Bundle written to: ${bundleConfig.to}`);
// Process included files (copy them)
if (bundleConfig.includeFiles && bundleConfig.includeFiles.length > 0) {
const htmlHandler = new HtmlHandler();
const outputDir = plugins.path.dirname(toPath);
for (const pattern of bundleConfig.includeFiles) {
await this.copyIncludedFiles(pattern, outputDir);
}
}
}
/**
* Copy files matching a pattern to the output directory
*/
private async copyIncludedFiles(pattern: string, outputDir: string): Promise<void> {
const absolutePattern = plugins.smartpath.transform.toAbsolute(pattern, this.cwd) as string;
const patternDir = plugins.path.dirname(absolutePattern);
const patternBase = plugins.path.basename(absolutePattern);
const isGlobPattern = patternBase.includes('*');
if (isGlobPattern) {
const dirPath = patternDir.replace(/\/\*\*$/, '');
const dirExists = await plugins.fs.directory(dirPath).exists();
if (!dirExists) {
console.log(`Directory does not exist: ${dirPath}`);
return;
}
const isRecursive = pattern.includes('**');
let entries;
if (isRecursive) {
entries = await plugins.fs.directory(dirPath).recursive().list();
} else {
entries = await plugins.fs.directory(dirPath).list();
}
const filePattern = patternBase.replace('*', '.*');
const regex = new RegExp(filePattern);
for (const entry of entries) {
if (!entry.isDirectory && regex.test(entry.name)) {
const fullPath = plugins.path.join(dirPath, entry.path);
const relativePath = plugins.path.relative(this.cwd, fullPath);
const destPath = plugins.path.join(outputDir, plugins.path.basename(entry.path));
await plugins.fs.directory(plugins.path.dirname(destPath)).create();
await plugins.fs.file(fullPath).copy(destPath);
console.log(`Copied: ${relativePath} -> ${destPath}`);
}
}
} else {
const fileExists = await plugins.fs.file(absolutePattern).exists();
if (!fileExists) {
console.log(`File does not exist: ${absolutePattern}`);
return;
}
const fileName = plugins.path.basename(absolutePattern);
const destPath = plugins.path.join(outputDir, fileName);
await plugins.fs.file(absolutePattern).copy(destPath);
console.log(`Copied: ${pattern} -> ${destPath}`);
}
}
}
/**
* Run the custom bundle command
*/
export async function runCustomBundles(): Promise<void> {
const handler = new CustomBundleHandler();
const hasConfig = await handler.loadConfig();
if (!hasConfig) {
return;
}
await handler.processAllBundles();
console.log('\nCustom bundle processing complete!');
}