Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@aws-sdk/types": "3.310.0",
"@swc/core": "1.3.56",
"@swc/jest": "0.2.26",
"@types/cli-progress": "^3.11.6",
"@types/node": "^18.15.11",
"@types/workerpool": "6.4.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
Expand All @@ -38,6 +39,7 @@
"license": "ISC",
"dependencies": {
"@aws-sdk/client-s3": "3.420.0",
"cli-progress": "^3.12.0",
"commander": "10.0.1",
"jszip": "3.10.1",
"typescript": "^5.0.4",
Expand Down
15 changes: 14 additions & 1 deletion src/classes/Builder/BuildHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ProjectStats, BuildParams, PackageJsonType, MissingProjectStats } from
import LocalCacher from '../Cache/LocalCacher';
import RemoteCacher from '../Cache/RemoteCacher';
import { configManagerInstance } from '../../config';
import { getBuildProgressBar, clearBuildProgressBar } from '../../utils/progressBar';

export default class BuildHelper extends WorkerHelper {
projects : Map<string, Set<string>> = new Map();
Expand Down Expand Up @@ -54,6 +55,8 @@ export default class BuildHelper extends WorkerHelper {

noCache = false;

progressBar = false;

cacher: RemoteCacher | LocalCacher;

hasher = new Hasher();
Expand All @@ -68,7 +71,7 @@ export default class BuildHelper extends WorkerHelper {
}

async init({
debug, compareWith, compareHash, logAffected, skipDependencies, onlyDependencies, debugLocation, skipPackageJson, singleCache, noCache, project, workspace
debug, compareWith, compareHash, logAffected, skipDependencies, onlyDependencies, debugLocation, skipPackageJson, singleCache, noCache, progressBar, project, workspace
}: BuildParams) : Promise<void> {
this.compareHash = compareHash;
this.logAffected = logAffected;
Expand All @@ -78,6 +81,7 @@ export default class BuildHelper extends WorkerHelper {
this.skipPackageJson = skipPackageJson;
this.singleCache = singleCache;
this.noCache = noCache;
this.progressBar = progressBar || false;
this.startTime = process.hrtime();
this.projectToBuild = project || 'all';
const constantDependencies = ConfigHelper.getConfig('mainConfig', '')[this.command]?.constantDependencies || [];
Expand Down Expand Up @@ -208,6 +212,10 @@ export default class BuildHelper extends WorkerHelper {

async buildResolver(project: string): Promise<void> {
this.removeProject(project);
const progressBarInstance = getBuildProgressBar();
if (progressBarInstance && this.progressBar) {
progressBarInstance.increment();
}
await this.build();
}

Expand Down Expand Up @@ -301,6 +309,11 @@ export default class BuildHelper extends WorkerHelper {
if (!projects.length) {
if (!stats.pendingTasks && !stats.activeTasks) {
void this.pool.terminate();
const progressBarInstance = getBuildProgressBar();
if (progressBarInstance && this.progressBar) {
progressBarInstance.stop();
clearBuildProgressBar();
}
Logger.log(2, this.outputColor, `Zenith completed command: ${this.command}. ${this.noCache ? '(Cache was not used)' : ''}`);
Logger.log(2, this.outputColor, `Total of ${this.totalCount} project${this.totalCount === 1 ? ' is' : 's are'} finished.`);
Logger.log(2, this.outputColor, `${this.fromCache} projects used from cache,`);
Expand Down
21 changes: 21 additions & 0 deletions src/classes/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Logger from '../utils/logger';
import { deepCloneMap } from '../utils/functions';
import { configManagerInstance } from '../config';
import { PipeConfigArray } from '../types/ConfigTypes';
import { createBuildProgressBar, clearBuildProgressBar } from '../utils/progressBar';

export default class Runner {
project = '';
Expand Down Expand Up @@ -38,6 +39,8 @@ export default class Runner {

coloredOutput = true;

progressBar = false;

static workspace = new Map<string, Set<string>>();

constructor(...args: readonly string[]) {
Expand All @@ -56,6 +59,7 @@ export default class Runner {
.option('-nc, --noCache', 'default: false. If true, will skip the cache and execute the target.')
.option('-np, --noPipe', 'default: false. If true, will skip the pipe and execute the target.')
.option('-co, --coloredOutput <color>', 'default: true. If false, will disable colors in the console.', 'true')
.option('-pb, --progressBar', 'default: false. If true, will show a progress bar during build.')
.addOption(
new Option(
'-l, --logLevel <logLevel>',
Expand Down Expand Up @@ -112,6 +116,9 @@ export default class Runner {
ZENITH_READ_ONLY: true
});
}
if (options.progressBar) {
this.progressBar = true;
}
this.debugLocation = options.debugLocation;
this.worker = options.worker;
this.pipe = options.noPipe ? [] : ConfigHelperInstance.pipe;
Expand Down Expand Up @@ -158,6 +165,7 @@ export default class Runner {
skipPackageJson: this.skipPackageJson,
singleCache: this.singleCache,
noCache: configManagerInstance.getConfigValue('ZENITH_NO_CACHE'),
progressBar: this.progressBar,
...config
};
const buildType = buildConfig.singleCache ? 'single' : 'project';
Expand All @@ -166,7 +174,20 @@ export default class Runner {
if (Runner.workspace.size === 0) {
Runner.workspace = deepCloneMap(Builder.getProjects());
}

// Create progress bar if enabled
if (this.progressBar) {
const totalProjects = Builder.getProjects().size;
const bar = createBuildProgressBar(totalProjects, `Zenith ${command}`);
bar.start();
}

Logger.log(2, Builder.outputColor, `Zenith ${command} started.`);
await Builder.build();

// Cleanup progress bar
if (this.progressBar) {
clearBuildProgressBar();
}
}
}
1 change: 1 addition & 0 deletions src/types/BuildTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface BuildParams {
skipPackageJson: boolean;
singleCache: boolean;
noCache: boolean;
progressBar?: boolean;
project: string;
workspace: Map<string, Set<string>>;
}
Expand Down
78 changes: 78 additions & 0 deletions src/utils/progressBar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as cliProgress from 'cli-progress';

export interface ProgressBarOptions {
total: number;
showPercentage?: boolean;
showETA?: boolean;
label?: string;
}

export class ProgressBar {
private bar: cliProgress.SingleBar;
private current = 0;
private total: number;
private enabled = true;

constructor(options: ProgressBarOptions) {
this.total = options.total;

this.bar = new cliProgress.SingleBar({
format: `${options.label || 'Progress'} |{bar}| {percentage}% | {value}/{total} | {status}`,
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true,
clearOnComplete: false,
stopOnComplete: true,
}, cliProgress.Presets.shades_classic);
}

start(): void {
if (!this.enabled) return;
this.bar.start(this.total, 0, { status: 'Starting...' });
}

update(value: number, status?: string): void {
if (!this.enabled) return;
this.current = value;
this.bar.update(value, { status: status || 'Building...' });
}

increment(status?: string): void {
if (!this.enabled) return;
this.current++;
this.bar.update(this.current, { status: status || 'Building...' });
}

stop(status?: string): void {
if (!this.enabled) return;
this.bar.update(this.total, { status: status || 'Complete!' });
this.bar.stop();
}

disable(): void {
this.enabled = false;
}

isEnabled(): boolean {
return this.enabled;
}
}

// Global progress bar instance for build tracking
let globalProgressBar: ProgressBar | null = null;

export function createBuildProgressBar(total: number, label = 'Building'): ProgressBar {
globalProgressBar = new ProgressBar({ total, label });
return globalProgressBar;
}

export function getBuildProgressBar(): ProgressBar | null {
return globalProgressBar;
}

export function clearBuildProgressBar(): void {
if (globalProgressBar) {
globalProgressBar.stop();
globalProgressBar = null;
}
}
125 changes: 125 additions & 0 deletions tests/progressBar.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const { ProgressBar, createBuildProgressBar, getBuildProgressBar, clearBuildProgressBar } = require('../build/utils/progressBar');

describe('ProgressBar', () => {
let progressBar;

beforeEach(() => {
progressBar = new ProgressBar({ total: 10, label: 'Test Progress' });
});

afterEach(() => {
if (progressBar) {
try {
progressBar.stop();
} catch (e) {
// Progress bar may already be stopped
}
}
clearBuildProgressBar();
});

describe('constructor', () => {
it('should create a progress bar with the given options', () => {
expect(progressBar).toBeDefined();
});

it('should be enabled by default', () => {
expect(progressBar.isEnabled()).toBe(true);
});
});

describe('disable', () => {
it('should disable the progress bar', () => {
progressBar.disable();
expect(progressBar.isEnabled()).toBe(false);
});
});

describe('start', () => {
it('should start the progress bar when enabled', () => {
expect(() => progressBar.start()).not.toThrow();
});

it('should not throw when disabled', () => {
progressBar.disable();
expect(() => progressBar.start()).not.toThrow();
});
});

describe('increment', () => {
it('should increment when enabled', () => {
progressBar.start();
expect(() => progressBar.increment()).not.toThrow();
});

it('should do nothing when disabled', () => {
progressBar.disable();
progressBar.start();
expect(() => progressBar.increment()).not.toThrow();
});
});

describe('update', () => {
it('should update the current value when enabled', () => {
progressBar.start();
expect(() => progressBar.update(5)).not.toThrow();
});

it('should do nothing when disabled', () => {
progressBar.disable();
progressBar.start();
expect(() => progressBar.update(5)).not.toThrow();
});
});

describe('stop', () => {
it('should stop the progress bar', () => {
progressBar.start();
expect(() => progressBar.stop()).not.toThrow();
});
});
});

describe('Global progress bar functions', () => {
afterEach(() => {
const bar = getBuildProgressBar();
if (bar) {
try {
bar.stop();
} catch (e) {
// Progress bar may already be stopped
}
}
clearBuildProgressBar();
});

describe('createBuildProgressBar', () => {
it('should create a new progress bar and store it globally', () => {
const bar = createBuildProgressBar(20, 'Build Test');
expect(bar).toBeDefined();
expect(bar.isEnabled()).toBe(true);
});

it('should be retrievable via getBuildProgressBar', () => {
const created = createBuildProgressBar(15, 'Retrieve Test');
const retrieved = getBuildProgressBar();
expect(retrieved).toBe(created);
});
});

describe('clearBuildProgressBar', () => {
it('should clear the global progress bar instance', () => {
createBuildProgressBar(10, 'Clear Test');
expect(getBuildProgressBar()).toBeDefined();
clearBuildProgressBar();
expect(getBuildProgressBar()).toBeNull();
});
});

describe('getBuildProgressBar', () => {
it('should return null when no progress bar is created', () => {
clearBuildProgressBar();
expect(getBuildProgressBar()).toBeNull();
});
});
});
16 changes: 15 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,13 @@
dependencies:
"@babel/types" "^7.20.7"

"@types/cli-progress@^3.11.6":
version "3.11.6"
resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.6.tgz#94b334ebe4190f710e51c1bf9b4fedb681fa9e45"
integrity sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==
dependencies:
"@types/node" "*"

"@types/graceful-fs@^4.1.2":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.7.tgz#30443a2e64fd51113bc3e2ba0914d47109695e2a"
Expand Down Expand Up @@ -2302,6 +2309,13 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==

cli-progress@^3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942"
integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==
dependencies:
string-width "^4.2.3"

cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
Expand Down Expand Up @@ -4568,7 +4582,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

string-width@^4.1.0, string-width@^4.2.0:
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down