feat(tsbundle): add npmextra-driven custom bundles, base64-ts output and interactive init wizard
This commit is contained in:
203
ts/mod_custom/index.ts
Normal file
203
ts/mod_custom/index.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
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!');
|
||||
}
|
||||
1
ts/mod_custom/plugins.ts
Normal file
1
ts/mod_custom/plugins.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from '../plugins.js';
|
||||
Reference in New Issue
Block a user