BREAKING(structure): modernize internal structure and support unpacking
This commit is contained in:
153
ts/mod_unpack/classes.tsunpacker.ts
Normal file
153
ts/mod_unpack/classes.tsunpacker.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { TsPublishConfig } from '../mod_config/index.js';
|
||||
import { FsHelpers } from '../mod_fs/index.js';
|
||||
|
||||
/**
|
||||
* TsUnpacker handles flattening of nested TypeScript output directories.
|
||||
*
|
||||
* When TypeScript compiles files that import from sibling directories,
|
||||
* it creates a nested structure like:
|
||||
* dist_ts_core/ts_core/index.js
|
||||
* dist_ts_core/ts_shared/helper.js
|
||||
*
|
||||
* This class flattens it to:
|
||||
* dist_ts_core/index.js
|
||||
*/
|
||||
export class TsUnpacker {
|
||||
private sourceFolderName: string;
|
||||
private destDir: string;
|
||||
private cwd: string;
|
||||
private config: TsPublishConfig;
|
||||
|
||||
constructor(sourceFolderName: string, destDir: string, cwd: string = process.cwd()) {
|
||||
this.sourceFolderName = sourceFolderName;
|
||||
this.destDir = destDir;
|
||||
this.cwd = cwd;
|
||||
this.config = new TsPublishConfig(path.join(cwd, sourceFolderName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unpacker from a glob pattern
|
||||
* './ts_core/**\/*.ts' → sourceFolderName = 'ts_core'
|
||||
*/
|
||||
public static fromGlobPattern(
|
||||
sourcePattern: string,
|
||||
destDir: string,
|
||||
cwd: string = process.cwd()
|
||||
): TsUnpacker | null {
|
||||
const sourceFolderName = FsHelpers.extractSourceFolder(sourcePattern);
|
||||
if (!sourceFolderName) {
|
||||
return null;
|
||||
}
|
||||
return new TsUnpacker(sourceFolderName, destDir, cwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source folder name
|
||||
*/
|
||||
public getSourceFolderName(): string {
|
||||
return this.sourceFolderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination directory
|
||||
*/
|
||||
public getDestDir(): string {
|
||||
return this.destDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if unpacking should be performed based on tspublish.json config
|
||||
* Default is true if not specified
|
||||
*/
|
||||
public async shouldUnpack(): Promise<boolean> {
|
||||
return this.config.shouldUnpack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if nested structure exists in the destination directory
|
||||
*/
|
||||
public async detectNesting(): Promise<boolean> {
|
||||
const nestedPath = path.join(this.destDir, this.sourceFolderName);
|
||||
return FsHelpers.directoryExists(nestedPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the nested directory
|
||||
*/
|
||||
public getNestedPath(): string {
|
||||
return path.join(this.destDir, this.sourceFolderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the unpack operation - flatten nested output directories
|
||||
* Returns true if unpacking was performed, false if skipped
|
||||
*/
|
||||
public async unpack(): Promise<boolean> {
|
||||
// Check if we should unpack based on config
|
||||
if (!(await this.shouldUnpack())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if nested structure exists
|
||||
if (!(await this.detectNesting())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nestedPath = this.getNestedPath();
|
||||
|
||||
// Delete sibling folders (not the source folder)
|
||||
await this.removeSiblingDirectories();
|
||||
|
||||
// Move contents from nested folder up
|
||||
await this.moveNestedContentsUp();
|
||||
|
||||
// Remove empty nested folder
|
||||
await FsHelpers.removeEmptyDirectory(nestedPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove sibling directories in the destination folder
|
||||
* (directories other than the source folder being unpacked)
|
||||
*/
|
||||
private async removeSiblingDirectories(): Promise<void> {
|
||||
const entries = await FsHelpers.listDirectory(this.destDir);
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory && entry.name !== this.sourceFolderName) {
|
||||
await FsHelpers.removeDirectory(path.join(this.destDir, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move contents from the nested folder up to the destination directory
|
||||
*/
|
||||
private async moveNestedContentsUp(): Promise<void> {
|
||||
const nestedPath = this.getNestedPath();
|
||||
const entries = await FsHelpers.listDirectory(nestedPath);
|
||||
for (const entry of entries) {
|
||||
const src = path.join(nestedPath, entry.name);
|
||||
const dest = path.join(this.destDir, entry.name);
|
||||
await FsHelpers.move(src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to perform unpack operation
|
||||
* Can be used directly without instantiating the class
|
||||
*/
|
||||
export async function performUnpack(
|
||||
sourcePattern: string,
|
||||
destDir: string,
|
||||
cwd: string = process.cwd()
|
||||
): Promise<boolean> {
|
||||
const unpacker = TsUnpacker.fromGlobPattern(sourcePattern, destDir, cwd);
|
||||
if (!unpacker) {
|
||||
return false;
|
||||
}
|
||||
return unpacker.unpack();
|
||||
}
|
||||
Reference in New Issue
Block a user