diff --git a/etc/fiddle-core.api.md b/etc/fiddle-core.api.md index d28462a..c361ca0 100644 --- a/etc/fiddle-core.api.md +++ b/etc/fiddle-core.api.md @@ -15,112 +15,77 @@ import { Writable } from 'stream'; // @public export class BaseVersions implements Versions { constructor(versions: unknown); - // (undocumented) getReleaseInfo(ver: SemOrStr): ReleaseInfo | undefined; - // (undocumented) inMajor(major: number): SemVer[]; - // (undocumented) inRange(a: SemOrStr, b: SemOrStr): SemVer[]; - // (undocumented) isVersion(ver: SemOrStr): boolean; - // (undocumented) get latest(): SemVer | undefined; - // (undocumented) get latestStable(): SemVer | undefined; - // (undocumented) get obsoleteMajors(): number[]; - // (undocumented) get prereleaseMajors(): number[]; - // (undocumented) protected setVersions(val: unknown): void; - // (undocumented) get stableMajors(): number[]; - // (undocumented) get supportedMajors(): number[]; - // (undocumented) get versions(): SemVer[]; } -// @public (undocumented) +// @public export interface BisectResult { - // (undocumented) range?: [string, string]; - // (undocumented) status: 'bisect_succeeded' | 'test_error' | 'system_error'; } -// @public (undocumented) +// @public export function compareVersions(a: SemVer, b: SemVer): number; -// @public (undocumented) +// @public export const DefaultPaths: Paths; -// @public (undocumented) +// @public export interface ElectronBinary { - // (undocumented) alreadyExtracted: boolean; - // (undocumented) path: string; } // @public export class ElectronVersions extends BaseVersions { - // (undocumented) static create(paths?: Partial, options?: ElectronVersionsCreateOptions): Promise; - // (undocumented) fetch(): Promise; - // (undocumented) inMajor(major: number): SemVer[]; - // (undocumented) inRange(a: SemOrStr, b: SemOrStr): SemVer[]; - // (undocumented) isVersion(ver: SemOrStr): boolean; - // (undocumented) get latest(): SemVer | undefined; - // (undocumented) get latestStable(): SemVer | undefined; - // (undocumented) get obsoleteMajors(): number[]; - // (undocumented) get prereleaseMajors(): number[]; - // (undocumented) get stableMajors(): number[]; - // (undocumented) get supportedMajors(): number[]; - // (undocumented) get versions(): SemVer[]; } -// @public (undocumented) +// @public export interface ElectronVersionsCreateOptions { ignoreCache?: boolean; initialVersions?: unknown; } -// @public (undocumented) +// @public export class Fiddle { - constructor(mainPath: string, // /path/to/main.js + constructor( + mainPath: string, // /path/to/main.js source: string); - // (undocumented) readonly mainPath: string; - // (undocumented) remove(): Promise; - // (undocumented) readonly source: string; } -// @public (undocumented) +// @public export class FiddleFactory { constructor(fiddles?: string); - // (undocumented) create(src: FiddleSource): Promise; - // (undocumented) fromEntries(src: Iterable<[string, string]>): Promise; - // (undocumented) fromFolder(source: string): Promise; - // (undocumented) fromGist(gistId: string): Promise; - // (undocumented) fromRepo(url: string, checkout?: string): Promise; } @@ -130,76 +95,56 @@ export type FiddleSource = Fiddle | string | Iterable<[string, string]>; // @public export class Installer extends EventEmitter { constructor(pathsIn?: Partial); - // (undocumented) ensureDownloaded(version: string, opts?: Partial): Promise; - // (undocumented) static execSubpath(platform?: string): string; - // (undocumented) static getExecPath(folder: string): string; - // (undocumented) install(version: string, opts?: Partial): Promise; get installedVersion(): string | undefined; remove(version: string): Promise; - // (undocumented) state(version: string): InstallState; } -// @public (undocumented) +// @public export interface InstallerParams { - // (undocumented) mirror: Mirrors; - // (undocumented) progressCallback: (progress: ProgressObject) => void; } // @public export enum InstallState { - // (undocumented) downloaded = "downloaded", - // (undocumented) downloading = "downloading", - // (undocumented) installed = "installed", - // (undocumented) installing = "installing", - // (undocumented) missing = "missing" } -// @public (undocumented) +// @public export interface InstallStateEvent { - // (undocumented) state: InstallState; - // (undocumented) version: string; } -// @public (undocumented) +// @public export interface Mirrors { - // (undocumented) electronMirror: string; - // (undocumented) electronNightlyMirror: string; } -// @public (undocumented) +// @public export interface Paths { - // (undocumented) readonly electronDownloads: string; - // (undocumented) readonly electronInstall: string; - // (undocumented) readonly fiddles: string; - // (undocumented) readonly versionsCache: string; } -// @public (undocumented) +// @public export type ProgressObject = { percent: number; }; -// @public (undocumented) +// @public export interface ReleaseInfo { chrome: string; date: string; @@ -213,51 +158,41 @@ export interface ReleaseInfo { zlib: string; } -// @public (undocumented) +// @public export function runFromCommandLine(argv: string[]): Promise; -// @public (undocumented) +// @public export class Runner { - // (undocumented) bisect(version_a: string | SemVer, version_b: string | SemVer, fiddleIn: FiddleSource, opts?: RunnerSpawnOptions): Promise; - // (undocumented) static create(opts?: { installer?: Installer; fiddleFactory?: FiddleFactory; paths?: Partial; versions?: Versions; }): Promise; - // (undocumented) static displayResult(result: TestResult): string; - // (undocumented) run(version: string | SemVer, fiddle: FiddleSource, opts?: RunnerSpawnOptions): Promise; - // (undocumented) spawn(versionIn: string | SemVer, fiddleIn: FiddleSource, opts?: RunnerSpawnOptions): Promise; } -// @public (undocumented) +// @public export interface RunnerOptions { - // (undocumented) args?: string[]; - // (undocumented) headless?: boolean; - // (undocumented) out?: Writable; - // (undocumented) showConfig?: boolean; } -// @public (undocumented) +// @public export type RunnerSpawnOptions = SpawnOptions & RunnerOptions; -// @public (undocumented) +// @public export type SemOrStr = SemVer | string; export { SemVer } -// @public (undocumented) +// @public export interface TestResult { - // (undocumented) status: 'test_passed' | 'test_failed' | 'test_error' | 'system_error'; } diff --git a/src/command-line.ts b/src/command-line.ts index a4ff0c9..b982331 100644 --- a/src/command-line.ts +++ b/src/command-line.ts @@ -5,6 +5,11 @@ import { ElectronVersions } from './versions'; import { Fiddle, FiddleFactory } from './fiddle'; import { Runner } from './runner'; +/** + * Function handles command-line arguments, creates instances of necessary objects and + * executes specific commands based on the arguments provided. + * It logs debug information and exits the process if invalid parameters are detected. + */ export async function runFromCommandLine(argv: string[]): Promise { const d = debug('fiddle-core:runFromCommandLine'); diff --git a/src/fiddle.ts b/src/fiddle.ts index 17b8864..81b8f3e 100644 --- a/src/fiddle.ts +++ b/src/fiddle.ts @@ -12,12 +12,16 @@ function hashString(str: string): string { return md5sum.digest('hex'); } +/** This class represents a fiddle */ export class Fiddle { constructor( + /** It serves as the entry point or the primary script file for the fiddle */ public readonly mainPath: string, // /path/to/main.js + /** Is the cource of the fiddle */ public readonly source: string, ) {} + /** This method deletes the fiddle from the system */ public remove(): Promise { return fs.remove(path.dirname(this.mainPath)); } @@ -31,13 +35,19 @@ export class Fiddle { */ export type FiddleSource = Fiddle | string | Iterable<[string, string]>; +/** + * This class is responsible for creating instances of the Fiddle class + * and it has methods to create a fiddle from various source + */ export class FiddleFactory { constructor(private readonly fiddles: string = DefaultPaths.fiddles) {} + /** This method creates a Fiddle instance by fetching a GitHub Gist and cloning it into a temporary directory, */ public async fromGist(gistId: string): Promise { return this.fromRepo(`https://gist.github.com/${gistId}.git`); } + /** This method creates a Fiddle instance by making a temporary copy of the fiddle from a specified source */ public async fromFolder(source: string): Promise { const d = debug('fiddle-core:FiddleFactory:fromFolder'); @@ -55,6 +65,11 @@ export class FiddleFactory { return new Fiddle(path.join(folder, 'main.js'), source); } + /** + * This method creates a Fiddle instance by cloning a Git repository into a temporary directory, + * optionally checking out a specified branch, + * and setting the main file path based on the cloned files. + */ public async fromRepo(url: string, checkout = 'master'): Promise { const d = debug('fiddle-core:FiddleFactory:fromRepo'); const folder = path.join(this.fiddles, hashString(url)); @@ -74,6 +89,10 @@ export class FiddleFactory { return new Fiddle(path.join(folder, 'main.js'), url); } + /** + * This method creates a Fiddle instance by saving a collection of filename-content pairs to a temporary directory + * and setting the main file path accordingly. + */ public async fromEntries(src: Iterable<[string, string]>): Promise { const d = debug('fiddle-core:FiddleFactory:fromEntries'); const map = new Map(src); @@ -95,6 +114,7 @@ export class FiddleFactory { return new Fiddle(path.join(folder, 'main.js'), 'entries'); } + /** This method determines the source type and calls the appropriate method to create the fiddle */ public async create(src: FiddleSource): Promise { if (src instanceof Fiddle) return src; diff --git a/src/installer.ts b/src/installer.ts index 4e8727b..96228f5 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -14,6 +14,7 @@ function getZipName(version: string): string { return `electron-v${version}-${process.platform}-${process.arch}.zip`; } +/** Tracks progress in percent */ export type ProgressObject = { percent: number }; /** @@ -22,30 +23,49 @@ export type ProgressObject = { percent: number }; * See Installer.on('state-changed') to watch for state changes. */ export enum InstallState { + /** Missing state */ missing = 'missing', + /** Downloading state */ downloading = 'downloading', + /** Downloaded state */ downloaded = 'downloaded', + /** Installing state */ installing = 'installing', + /** Installed state */ installed = 'installed', } +/** + * An event that indicates a change in the installation state of a version + */ export interface InstallStateEvent { + /** The version of the installation */ version: string; + /** The state of the installation */ state: InstallState; } +/** An object that holds information about electron mirrors */ export interface Mirrors { + /** URL of the electron mirror */ electronMirror: string; + /** URL of the electron nightly mirror */ electronNightlyMirror: string; } +/** Configuration for an Electron binary */ export interface ElectronBinary { + /** Path to the Electron binary */ path: string; + /** Checks whether the Electron binary is already extracted or not */ alreadyExtracted: boolean; // to check if it's kept as zipped or not } +/** Parameters for an installer */ export interface InstallerParams { + /** Callback function to receive progress updates */ progressCallback: (progress: ProgressObject) => void; + /** Mirrors to use for the installation */ mirror: Mirrors; } @@ -71,6 +91,7 @@ export class Installer extends EventEmitter { this.rebuildStates(); } + /** Returns the executable subpath based on the platform */ public static execSubpath(platform: string = process.platform): string { switch (platform) { case 'darwin': @@ -82,10 +103,12 @@ export class Installer extends EventEmitter { } } + /** Executable path for a given folder */ public static getExecPath(folder: string): string { return path.join(folder, Installer.execSubpath()); } + /** Installation state for a specific version */ public state(version: string): InstallState { return this.stateMap.get(version) || InstallState.missing; } @@ -301,6 +324,9 @@ export class Installer extends EventEmitter { /** map of version string to currently-running active Promise */ private downloading = new Map>(); + /** + * Ensures that the specified version of Electron is downloaded and available. + */ public async ensureDownloaded( version: string, opts?: Partial, @@ -319,6 +345,7 @@ export class Installer extends EventEmitter { /** keep a track of all currently installing versions */ private installing = new Set(); + /** Installs the specified version of Electron */ public async install( version: string, opts?: Partial, diff --git a/src/paths.ts b/src/paths.ts index 17e98d3..dab5d6e 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -1,22 +1,24 @@ import * as path from 'path'; import envPaths from 'env-paths'; +/** Paths used by fiddle-core */ export interface Paths { - // folder where electron zipfiles will be cached + /** folder where electron zipfiles will be cached */ readonly electronDownloads: string; - // folder where an electron download will be unzipped to be run + /** folder where an electron download will be unzipped to be run */ readonly electronInstall: string; - // folder where fiddles will be saved + /** folder where fiddles will be saved */ readonly fiddles: string; - // file where electron releases are cached + /** file where electron releases are cached */ readonly versionsCache: string; } const paths = envPaths('fiddle-core', { suffix: '' }); +/** Default paths. */ export const DefaultPaths: Paths = { electronDownloads: path.join(paths.data, 'electron', 'zips'), electronInstall: path.join(paths.data, 'electron', 'current'), diff --git a/src/runner.ts b/src/runner.ts index bf4e5c7..fb5a68a 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -13,14 +13,15 @@ import { ElectronVersions, Versions } from './versions'; import { Fiddle, FiddleFactory, FiddleSource } from './fiddle'; import { DefaultPaths, Paths } from './paths'; +/** Represents the options for a runner */ export interface RunnerOptions { - // extra arguments to be appended to the electron invocation + /** Extra arguments to be appended to the electron invocation */ args?: string[]; - // if true, use xvfb-run on *nix + /** if true, use xvfb-run on *nix */ headless?: boolean; - // where the test's output should be written + /** where the test's output should be written */ out?: Writable; - // whether to show config info (e.g. platform os & arch) in the log + /** whether to show config info (e.g. platform os & arch) in the log */ showConfig?: boolean; } @@ -31,17 +32,24 @@ const DefaultRunnerOpts: RunnerOptions = { showConfig: true, } as const; +/** Options for spawning a runner */ export type RunnerSpawnOptions = SpawnOptions & RunnerOptions; +/** Result of a test */ export interface TestResult { + /** Status of the test result */ status: 'test_passed' | 'test_failed' | 'test_error' | 'system_error'; } +/** Result of a bisect operation */ export interface BisectResult { + /** The range of values where the bisect operation succeeded */ range?: [string, string]; + /** The status of the bisect operation */ status: 'bisect_succeeded' | 'test_error' | 'system_error'; } +/** Executing Electron-related tasks */ export class Runner { private osInfo = ''; @@ -53,6 +61,7 @@ export class Runner { getos((err, result) => (this.osInfo = inspect(result || err))); } + /** Creates a new instance of the Runner class */ public static async create( opts: { installer?: Installer; @@ -132,6 +141,7 @@ export class Runner { return { exec, args }; } + /** Spawns a child process and run a fiddle with Electron */ public async spawn( versionIn: string | SemVer, fiddleIn: FiddleSource, @@ -179,6 +189,7 @@ export class Runner { } } + /** Displays result */ public static displayResult(result: TestResult): string { const text = Runner.displayEmoji(result); switch (result.status) { @@ -193,6 +204,7 @@ export class Runner { } } + /** Runs a fiddle with a specific version of Electron and obtain the test result */ public async run( version: string | SemVer, fiddle: FiddleSource, @@ -213,6 +225,7 @@ export class Runner { }); } + /** Bisects operation between two versions of Electron to find a regression */ public async bisect( version_a: string | SemVer, version_b: string | SemVer, diff --git a/src/versions.ts b/src/versions.ts index 8448fff..8ecfac8 100644 --- a/src/versions.ts +++ b/src/versions.ts @@ -7,8 +7,10 @@ export { SemVer }; import { DefaultPaths, Paths } from './paths'; +/** Type that can be either a SemVer object or a string */ export type SemOrStr = SemVer | string; +/** Information about a release */ export interface ReleaseInfo { /** Electron version */ version: string; @@ -70,6 +72,9 @@ export interface Versions { getReleaseInfo(version: SemOrStr): ReleaseInfo | undefined; } +/** + * Represents the options for creating Electron versions. + */ export interface ElectronVersionsCreateOptions { /** Initial versions to use if there is no cache. When provided, no initial fetch is done */ initialVersions?: unknown; @@ -78,6 +83,7 @@ export interface ElectronVersionsCreateOptions { ignoreCache?: boolean; } +/** Compares two semantic version numbers */ export function compareVersions(a: SemVer, b: SemVer): number { const l = a.compareMain(b); if (l) return l; @@ -150,6 +156,7 @@ export class BaseVersions implements Versions { private readonly map = new Map(); private readonly releaseInfo = new Map(); + /** Sets the versions and their corresponding release information */ protected setVersions(val: unknown): void { // release info doesn't need to be in sorted order this.releaseInfo.clear(); @@ -193,6 +200,7 @@ export class BaseVersions implements Versions { this.setVersions(versions); } + /** An array of major versions that have prerelease versions */ public get prereleaseMajors(): number[] { const majors = new Set(); for (const ver of this.map.values()) { @@ -206,6 +214,7 @@ export class BaseVersions implements Versions { return [...majors]; } + /** An array of major versions that only have stable versions */ public get stableMajors(): number[] { const majors = new Set(); for (const ver of this.map.values()) { @@ -216,22 +225,27 @@ export class BaseVersions implements Versions { return [...majors]; } + /** An array of the most recently supported major versions */ public get supportedMajors(): number[] { return this.stableMajors.slice(-NUM_SUPPORTED_MAJORS); } + /** An array of major versions that are considered obsolete */ public get obsoleteMajors(): number[] { return this.stableMajors.slice(0, -NUM_SUPPORTED_MAJORS); } + /** An array of all semantic versions */ public get versions(): SemVer[] { return [...this.map.values()]; } + /** Latest semantic version */ public get latest(): SemVer | undefined { return this.versions.pop(); } + /** Latest stable semantic version */ public get latestStable(): SemVer | undefined { let stable: SemVer | undefined = undefined; for (const ver of this.map.values()) { @@ -242,10 +256,12 @@ export class BaseVersions implements Versions { return stable; } + /** Checks if a given value is a valid version */ public isVersion(ver: SemOrStr): boolean { return this.map.has(typeof ver === 'string' ? ver : ver.version); } + /** An array of semantic versions in a specific major version */ public inMajor(major: number): SemVer[] { const versions: SemVer[] = []; for (const ver of this.map.values()) { @@ -256,6 +272,7 @@ export class BaseVersions implements Versions { return versions; } + /** Gets an array of semantic versions within a specified range */ public inRange(a: SemOrStr, b: SemOrStr): SemVer[] { if (typeof a !== 'string') a = a.version; if (typeof b !== 'string') b = b.version; @@ -267,6 +284,7 @@ export class BaseVersions implements Versions { return versions.slice(first, last + 1); } + /** Gets the release information for a given version */ public getReleaseInfo(ver: SemOrStr): ReleaseInfo | undefined { return this.releaseInfo.get(typeof ver === 'string' ? ver : ver.version); } @@ -302,6 +320,7 @@ export class ElectronVersions extends BaseVersions { return now <= cacheTimeMs + VERSION_CACHE_TTL_MS; } + /** Creates an instance of the parent class. */ public static async create( paths: Partial = {}, options: ElectronVersionsCreateOptions = {}, @@ -335,7 +354,7 @@ export class ElectronVersions extends BaseVersions { return new ElectronVersions(versionsCache, now, versions); } - // update the cache + /** update the cache */ public async fetch(): Promise { const d = debug('fiddle-core:ElectronVersions:fetch'); const { mtimeMs, versionsCache } = this; @@ -357,42 +376,61 @@ export class ElectronVersions extends BaseVersions { } } + /** Getter to ensure the cache is up to date */ public override get prereleaseMajors(): number[] { void this.keepFresh(); return super.prereleaseMajors; } + + /** Getter to ensure the cache is up to date */ public override get stableMajors(): number[] { void this.keepFresh(); return super.stableMajors; } + + /** Getter to ensure the cache is up to date */ public override get supportedMajors(): number[] { void this.keepFresh(); return super.supportedMajors; } + + /** Getter to ensure the cache is up to date */ public override get obsoleteMajors(): number[] { void this.keepFresh(); return super.obsoleteMajors; } + + /** Getter to ensure the cache is up to date */ public override get versions(): SemVer[] { void this.keepFresh(); return super.versions; } + + /** Getter to ensure the cache is up to date */ public override get latest(): SemVer | undefined { void this.keepFresh(); return super.latest; } + + /** Getter to ensure the cache is up to date. */ public override get latestStable(): SemVer | undefined { void this.keepFresh(); return super.latestStable; } + + /** An method to ensure the cache is up to date */ public override isVersion(ver: SemOrStr): boolean { void this.keepFresh(); return super.isVersion(ver); } + + /** An method to ensure the cache is up to date */ public override inMajor(major: number): SemVer[] { void this.keepFresh(); return super.inMajor(major); } + + /** An method to ensure the cache is up to date */ public override inRange(a: SemOrStr, b: SemOrStr): SemVer[] { void this.keepFresh(); return super.inRange(a, b);