diff --git a/apps/cpu-profile-summarizer/src/start.ts b/apps/cpu-profile-summarizer/src/start.ts index 8360ef05e9f..d7a3474d066 100644 --- a/apps/cpu-profile-summarizer/src/start.ts +++ b/apps/cpu-profile-summarizer/src/start.ts @@ -119,8 +119,8 @@ function writeSummaryToTsv(tsvPath: string, summary: IProfileSummary): void { } class CpuProfileSummarizerCommandLineParser extends CommandLineParser { - private readonly _inputParameter: CommandLineStringListParameter; - private readonly _outputParameter: IRequiredCommandLineStringParameter; + readonly #inputParameter: CommandLineStringListParameter; + readonly #outputParameter: IRequiredCommandLineStringParameter; public constructor() { super({ @@ -130,7 +130,7 @@ class CpuProfileSummarizerCommandLineParser extends CommandLineParser { 'For example, those generated by running `node --cpu-prof`.' }); - this._inputParameter = this.defineStringListParameter({ + this.#inputParameter = this.defineStringListParameter({ parameterLongName: '--input', parameterShortName: '-i', description: 'The directory containing .cpuprofile files to summarize', @@ -138,7 +138,7 @@ class CpuProfileSummarizerCommandLineParser extends CommandLineParser { required: true }); - this._outputParameter = this.defineStringParameter({ + this.#outputParameter = this.defineStringParameter({ parameterLongName: '--output', parameterShortName: '-o', description: 'The output file to write the summary to', @@ -148,8 +148,8 @@ class CpuProfileSummarizerCommandLineParser extends CommandLineParser { } protected override async onExecuteAsync(): Promise { - const input: readonly string[] = this._inputParameter.values; - const output: string = this._outputParameter.value; + const input: readonly string[] = this.#inputParameter.values; + const output: string = this.#outputParameter.value; if (input.length === 0) { throw new Error('No input directories provided'); diff --git a/apps/lockfile-explorer/src/cli/explorer/ExplorerCommandLineParser.ts b/apps/lockfile-explorer/src/cli/explorer/ExplorerCommandLineParser.ts index fab4cb5765b..84c811b6206 100644 --- a/apps/lockfile-explorer/src/cli/explorer/ExplorerCommandLineParser.ts +++ b/apps/lockfile-explorer/src/cli/explorer/ExplorerCommandLineParser.ts @@ -51,8 +51,8 @@ function printUpdateNotification( export class ExplorerCommandLineParser extends CommandLineParser { public readonly globalTerminal: ITerminal; - private readonly _debugParameter: CommandLineFlagParameter; - private readonly _subspaceParameter: IRequiredCommandLineStringParameter; + readonly #debugParameter: CommandLineFlagParameter; + readonly #subspaceParameter: IRequiredCommandLineStringParameter; public constructor(terminal: ITerminal) { super({ @@ -61,13 +61,13 @@ export class ExplorerCommandLineParser extends CommandLineParser { 'Lockfile Explorer is a desktop app for investigating and solving version conflicts in a PNPM workspace.' }); - this._debugParameter = this.defineFlagParameter({ + this.#debugParameter = this.defineFlagParameter({ parameterLongName: '--debug', parameterShortName: '-d', description: 'Show the full call stack if an error occurs while executing the tool' }); - this._subspaceParameter = this.defineStringParameter({ + this.#subspaceParameter = this.defineStringParameter({ parameterLongName: '--subspace', argumentName: 'SUBSPACE_NAME', description: 'Specifies an individual Rush subspace to check.', @@ -78,7 +78,7 @@ export class ExplorerCommandLineParser extends CommandLineParser { } public get isDebug(): boolean { - return this._debugParameter.value; + return this.#debugParameter.value; } protected override async onExecuteAsync(): Promise { @@ -106,7 +106,7 @@ export class ExplorerCommandLineParser extends CommandLineParser { const appState: IAppState = init({ appVersion: LFX_VERSION, debugMode: this.isDebug, - subspaceName: this._subspaceParameter.value + subspaceName: this.#subspaceParameter.value }); const lfxWorkspace: IJsonLfxWorkspace = appState.lfxWorkspace; diff --git a/apps/lockfile-explorer/src/cli/lint/LintCommandLineParser.ts b/apps/lockfile-explorer/src/cli/lint/LintCommandLineParser.ts index e7f4b8d6a33..7b306a01d26 100644 --- a/apps/lockfile-explorer/src/cli/lint/LintCommandLineParser.ts +++ b/apps/lockfile-explorer/src/cli/lint/LintCommandLineParser.ts @@ -22,7 +22,7 @@ export class LintCommandLineParser extends CommandLineParser { this.globalTerminal = terminal; - this._populateActions(); + this.#populateActions(); } protected override async onExecuteAsync(): Promise { @@ -33,7 +33,7 @@ export class LintCommandLineParser extends CommandLineParser { await super.onExecuteAsync(); } - private _populateActions(): void { + #populateActions(): void { const terminal: ITerminal = this.globalTerminal; this.addAction(new InitAction(terminal)); this.addAction(new CheckAction(terminal)); diff --git a/apps/lockfile-explorer/src/cli/lint/actions/CheckAction.ts b/apps/lockfile-explorer/src/cli/lint/actions/CheckAction.ts index a7b7347788f..99e2cf96680 100644 --- a/apps/lockfile-explorer/src/cli/lint/actions/CheckAction.ts +++ b/apps/lockfile-explorer/src/cli/lint/actions/CheckAction.ts @@ -38,11 +38,11 @@ export interface ILintIssue { } export class CheckAction extends CommandLineAction { - private readonly _terminal: ITerminal; + readonly #terminal: ITerminal; - private _rushConfiguration!: RushConfiguration; - private _checkedProjects: Set; - private _docMap: Map; + #rushConfiguration!: RushConfiguration; + #checkedProjects: Set; + #docMap: Map; public constructor(terminal: ITerminal) { super({ @@ -54,12 +54,12 @@ export class CheckAction extends CommandLineAction { ', reporting any problems found in your PNPM workspace.' }); - this._terminal = terminal; - this._checkedProjects = new Set(); - this._docMap = new Map(); + this.#terminal = terminal; + this.#checkedProjects = new Set(); + this.#docMap = new Map(); } - private async _checkVersionCompatibilityAsync( + async #checkVersionCompatibilityAsync( shrinkwrapFileMajorVersion: number, packages: lockfileTypes.PackageSnapshots | undefined, dependencyPath: pnpmTypes.DepPath, @@ -79,7 +79,7 @@ export class CheckAction extends CommandLineAction { await Promise.all( Object.entries(packages[dependencyPath].dependencies ?? {}).map( async ([dependencyPackageName, dependencyPackageVersion]) => { - await this._checkVersionCompatibilityAsync( + await this.#checkVersionCompatibilityAsync( shrinkwrapFileMajorVersion, packages, splicePackageWithVersion( @@ -96,22 +96,22 @@ export class CheckAction extends CommandLineAction { } } - private async _searchAndValidateDependenciesAsync( + async #searchAndValidateDependenciesAsync( project: RushConfigurationProject, requiredVersions: Record ): Promise { - this._terminal.writeLine(`Checking project "${project.packageName}"`); + this.#terminal.writeLine(`Checking project "${project.packageName}"`); const projectFolder: string = project.projectFolder; const subspace: Subspace = project.subspace; const shrinkwrapFilename: string = subspace.getCommittedShrinkwrapFilePath(); let doc: lockfileTypes.LockfileObject; - if (this._docMap.has(shrinkwrapFilename)) { - doc = this._docMap.get(shrinkwrapFilename)!; + if (this.#docMap.has(shrinkwrapFilename)) { + doc = this.#docMap.get(shrinkwrapFilename)!; } else { const pnpmLockfileText: string = await FileSystem.readFileAsync(shrinkwrapFilename); doc = yaml.load(pnpmLockfileText) as lockfileTypes.LockfileObject; - this._docMap.set(shrinkwrapFilename, doc); + this.#docMap.set(shrinkwrapFilename, doc); } const { importers, lockfileVersion, packages } = doc; const shrinkwrapFileMajorVersion: number = getShrinkwrapFileMajorVersion(lockfileVersion); @@ -136,13 +136,13 @@ export class CheckAction extends CommandLineAction { ) as pnpmTypes.DepPath; if (fullDependencyPath.includes('link:')) { const dependencyProject: RushConfigurationProject | undefined = - this._rushConfiguration.getProjectByName(dependencyName); - if (dependencyProject && !this._checkedProjects?.has(dependencyProject)) { - this._checkedProjects!.add(project); - await this._searchAndValidateDependenciesAsync(dependencyProject, requiredVersions); + this.#rushConfiguration.getProjectByName(dependencyName); + if (dependencyProject && !this.#checkedProjects?.has(dependencyProject)) { + this.#checkedProjects!.add(project); + await this.#searchAndValidateDependenciesAsync(dependencyProject, requiredVersions); } } else { - await this._checkVersionCompatibilityAsync( + await this.#checkVersionCompatibilityAsync( shrinkwrapFileMajorVersion, packages, fullDependencyPath, @@ -156,20 +156,20 @@ export class CheckAction extends CommandLineAction { ); } - private async _performVersionRestrictionCheckAsync( + async #performVersionRestrictionCheckAsync( requiredVersions: Record, projectName: string ): Promise { try { const project: RushConfigurationProject | undefined = - this._rushConfiguration?.getProjectByName(projectName); + this.#rushConfiguration?.getProjectByName(projectName); if (!project) { throw new Error( `Specified project "${projectName}" does not exist in ${LOCKFILE_LINT_JSON_FILENAME}` ); } - this._checkedProjects.add(project); - await this._searchAndValidateDependenciesAsync(project, requiredVersions); + this.#checkedProjects.add(project); + await this.#searchAndValidateDependenciesAsync(project, requiredVersions); return undefined; } catch (e) { return e.message; @@ -183,10 +183,10 @@ export class CheckAction extends CommandLineAction { 'The "lockfile-explorer check" must be executed in a folder that is under a Rush workspace folder' ); } - this._rushConfiguration = rushConfiguration!; + this.#rushConfiguration = rushConfiguration!; const lintingFile: string = path.resolve( - this._rushConfiguration.commonFolder, + this.#rushConfiguration.commonFolder, 'config', LOCKFILE_EXPLORER_FOLDERNAME, LOCKFILE_LINT_JSON_FILENAME @@ -201,7 +201,7 @@ export class CheckAction extends CommandLineAction { async ({ requiredVersions, project, rule }) => { switch (rule) { case 'restrict-versions': { - const message: string | undefined = await this._performVersionRestrictionCheckAsync( + const message: string | undefined = await this.#performVersionRestrictionCheckAsync( requiredVersions, project ); @@ -219,7 +219,7 @@ export class CheckAction extends CommandLineAction { { concurrency: 50 } ); if (issues.length > 0) { - this._terminal.writeLine(); + this.#terminal.writeLine(); // Deterministic order for (const issue of issues.sort((a, b): number => { @@ -233,13 +233,13 @@ export class CheckAction extends CommandLineAction { } return a.message.localeCompare(b.message); })) { - this._terminal.writeLine( + this.#terminal.writeLine( Colorize.red('PROBLEM: ') + Colorize.cyan(`[${issue.rule}] `) + issue.message + '\n' ); } throw new AlreadyReportedError(); } - this._terminal.writeLine(Colorize.green('SUCCESS: ') + 'All checks passed.'); + this.#terminal.writeLine(Colorize.green('SUCCESS: ') + 'All checks passed.'); } } diff --git a/apps/lockfile-explorer/src/cli/lint/actions/InitAction.ts b/apps/lockfile-explorer/src/cli/lint/actions/InitAction.ts index 834f7761f55..f45a0587c1d 100644 --- a/apps/lockfile-explorer/src/cli/lint/actions/InitAction.ts +++ b/apps/lockfile-explorer/src/cli/lint/actions/InitAction.ts @@ -11,7 +11,7 @@ import { FileSystem } from '@rushstack/node-core-library'; import { LOCKFILE_EXPLORER_FOLDERNAME, LOCKFILE_LINT_JSON_FILENAME } from '../../../constants/common'; export class InitAction extends CommandLineAction { - private readonly _terminal: ITerminal; + readonly #terminal: ITerminal; public constructor(terminal: ITerminal) { super({ @@ -21,7 +21,7 @@ export class InitAction extends CommandLineAction { `This command initializes a new ${LOCKFILE_LINT_JSON_FILENAME} config file.` + ` The created template file includes source code comments that document the settings.` }); - this._terminal = terminal; + this.#terminal = terminal; } protected override async onExecuteAsync(): Promise { @@ -43,12 +43,12 @@ export class InitAction extends CommandLineAction { ); if (await FileSystem.existsAsync(outputFilePath)) { - this._terminal.writeError('The output file already exists:'); - this._terminal.writeLine('\n ' + outputFilePath + '\n'); + this.#terminal.writeError('The output file already exists:'); + this.#terminal.writeLine('\n ' + outputFilePath + '\n'); throw new Error('Unable to write output file'); } - this._terminal.writeLine(Colorize.green('Writing file: ') + outputFilePath); + this.#terminal.writeLine(Colorize.green('Writing file: ') + outputFilePath); await FileSystem.copyFileAsync({ sourcePath: inputFilePath, destinationPath: outputFilePath diff --git a/apps/lockfile-explorer/src/graph/PnpmfileRunner.ts b/apps/lockfile-explorer/src/graph/PnpmfileRunner.ts index 10c09e7d40b..3f7b8ef8fdb 100644 --- a/apps/lockfile-explorer/src/graph/PnpmfileRunner.ts +++ b/apps/lockfile-explorer/src/graph/PnpmfileRunner.ts @@ -18,27 +18,27 @@ interface IPromise { * package.json files. Calling `disposeAsync()` will free the loaded modules. */ export class PnpmfileRunner { - private _worker: Worker; - private _nextId: number = 1000; - private _promisesById: Map = new Map(); - private _disposed: boolean = false; + #worker: Worker; + #nextId: number = 1000; + #promisesById: Map = new Map(); + #disposed: boolean = false; public logger: ((message: string) => void) | undefined = undefined; public constructor(pnpmfilePath: string) { - this._worker = new Worker(path.join(`${__dirname}/pnpmfileRunnerWorkerThread.js`), { + this.#worker = new Worker(path.join(`${__dirname}/pnpmfileRunnerWorkerThread.js`), { workerData: { pnpmfilePath } }); - this._worker.on('message', (message: ResponseMessage) => { + this.#worker.on('message', (message: ResponseMessage) => { const id: number = message.id; - const promise: IPromise | undefined = this._promisesById.get(id); + const promise: IPromise | undefined = this.#promisesById.get(id); if (!promise) { return; } if (message.kind === 'return') { - this._promisesById.delete(id); + this.#promisesById.delete(id); // TODO: Validate the user's readPackage() return value const result: IPackageJson = message.result as IPackageJson; promise.resolve(result); @@ -50,28 +50,28 @@ export class PnpmfileRunner { console.log('.pnpmfile.cjs: ' + message.log); } } else { - this._promisesById.delete(id); + this.#promisesById.delete(id); promise.reject(new Error(message.error || 'An unknown error occurred')); } }); - this._worker.on('error', (err) => { - for (const promise of this._promisesById.values()) { + this.#worker.on('error', (err) => { + for (const promise of this.#promisesById.values()) { promise.reject(err); } - this._promisesById.clear(); + this.#promisesById.clear(); }); - this._worker.on('exit', (code) => { - if (!this._disposed) { + this.#worker.on('exit', (code) => { + if (!this.#disposed) { const error: Error = new Error( `PnpmfileRunner worker thread terminated unexpectedly with exit code ${code}` ); console.error(error); - for (const promise of this._promisesById.values()) { + for (const promise of this.#promisesById.values()) { promise.reject(error); } - this._promisesById.clear(); + this.#promisesById.clear(); } }); } @@ -83,26 +83,26 @@ export class PnpmfileRunner { packageJson: IPackageJson, packageJsonFullPath: string ): Promise { - if (this._disposed) { + if (this.#disposed) { return Promise.reject(new Error('The operation failed because PnpmfileRunner has been disposed')); } - const id: number = this._nextId++; + const id: number = this.#nextId++; return new Promise((resolve, reject) => { - this._promisesById.set(id, { resolve, reject }); - this._worker.postMessage({ id, packageJson, packageJsonFullPath } satisfies IRequestMessage); + this.#promisesById.set(id, { resolve, reject }); + this.#worker.postMessage({ id, packageJson, packageJsonFullPath } satisfies IRequestMessage); }); } public async disposeAsync(): Promise { - if (this._disposed) { + if (this.#disposed) { return; } - for (const pending of this._promisesById.values()) { + for (const pending of this.#promisesById.values()) { pending.reject(new Error('Aborted because PnpmfileRunner was disposed')); } - this._promisesById.clear(); - this._disposed = true; - await this._worker.terminate(); + this.#promisesById.clear(); + this.#disposed = true; + await this.#worker.terminate(); } } diff --git a/apps/lockfile-explorer/src/utils/PackageUpdateChecker.ts b/apps/lockfile-explorer/src/utils/PackageUpdateChecker.ts index cf353b347d2..fbbd87da8b4 100644 --- a/apps/lockfile-explorer/src/utils/PackageUpdateChecker.ts +++ b/apps/lockfile-explorer/src/utils/PackageUpdateChecker.ts @@ -140,11 +140,11 @@ async function _writeCacheAsync( * @internal */ export class PackageUpdateChecker { - private readonly _packageName: string; - private readonly _currentVersion: string; - private readonly _skip: boolean; - private readonly _forceCheck: boolean; - private readonly _cacheExpiryMs: number; + readonly #packageName: string; + readonly #currentVersion: string; + readonly #skip: boolean; + readonly #forceCheck: boolean; + readonly #cacheExpiryMs: number; public constructor(options: IPackageUpdateCheckerOptions) { const { @@ -154,11 +154,11 @@ export class PackageUpdateChecker { forceCheck = false, cacheExpiryMs = DEFAULT_CACHE_EXPIRY_MS } = options; - this._packageName = packageName; - this._currentVersion = currentVersion; - this._skip = skip; - this._forceCheck = forceCheck; - this._cacheExpiryMs = cacheExpiryMs; + this.#packageName = packageName; + this.#currentVersion = currentVersion; + this.#skip = skip; + this.#forceCheck = forceCheck; + this.#cacheExpiryMs = cacheExpiryMs; } /** @@ -166,19 +166,19 @@ export class PackageUpdateChecker { * was skipped or the registry could not be reached. */ public async tryGetUpdateAsync(): Promise { - if (this._skip) { + if (this.#skip) { return undefined; } - const cacheFilePath: string = this._getCacheFilePath(); + const cacheFilePath: string = this.#getCacheFilePath(); let latestVersion: string | undefined; - if (!this._forceCheck) { + if (!this.#forceCheck) { const cached: IUpdateCheckCache | undefined = await _readCacheAsync(cacheFilePath); if (cached !== undefined) { const { checkedAt, latestVersion: latestVersionFromCache } = cached; const ageMs: number = Date.now() - checkedAt; - if (ageMs < this._cacheExpiryMs) { + if (ageMs < this.#cacheExpiryMs) { latestVersion = latestVersionFromCache; } } @@ -186,7 +186,7 @@ export class PackageUpdateChecker { if (latestVersion === undefined) { // Cache is missing or stale — fetch from the registry. - latestVersion = await _tryFetchLatestVersionAsync(this._packageName); + latestVersion = await _tryFetchLatestVersionAsync(this.#packageName); if (latestVersion === undefined) { return undefined; } @@ -196,13 +196,13 @@ export class PackageUpdateChecker { return { latestVersion, - isOutdated: semver.gt(latestVersion, this._currentVersion) + isOutdated: semver.gt(latestVersion, this.#currentVersion) }; } - private _getCacheFilePath(): string { + #getCacheFilePath(): string { // Replace characters that are unsafe in file names (e.g. the "/" in scoped package names). - const sanitizedName: string = this._packageName.replace(/[^a-zA-Z0-9._-]/g, '_'); + const sanitizedName: string = this.#packageName.replace(/[^a-zA-Z0-9._-]/g, '_'); return `${CACHE_FOLDER}/${sanitizedName}.json`; } } diff --git a/apps/playwright-browser-tunnel/src/HttpServer.ts b/apps/playwright-browser-tunnel/src/HttpServer.ts index ced852a5f98..7942d79bfbe 100644 --- a/apps/playwright-browser-tunnel/src/HttpServer.ts +++ b/apps/playwright-browser-tunnel/src/HttpServer.ts @@ -28,22 +28,22 @@ function formatAddress(addressInfo: AddressInfo): string { * browserName and launchOptions. */ export class HttpServer { - private readonly _server: http.Server; - private readonly _wsServer: WebSocketServer; // local proxy websocket server accepting browser clients - private _listeningAddress: string | undefined; - private _logger: ITerminal; + readonly #server: http.Server; + readonly #wsServer: WebSocketServer; // local proxy websocket server accepting browser clients + #listeningAddress: string | undefined; + #logger: ITerminal; public constructor(logger: ITerminal) { - this._logger = logger; + this.#logger = logger; // We'll create an HTTP server and attach a WebSocketServer in noServer mode so we can // manually parse the URL and extract query parameters before upgrading. - this._server = http.createServer(); - this._wsServer = new WebSocketServer({ noServer: true }); + this.#server = http.createServer(); + this.#wsServer = new WebSocketServer({ noServer: true }); - this._server.on('upgrade', (request, socket, head) => { + this.#server.on('upgrade', (request, socket, head) => { // Accept all upgrades on the root path. We parse query string for browserName + launchOptions. - this._wsServer.handleUpgrade(request, socket, head, (ws: WebSocket) => { - this._wsServer.emit('connection', ws, request); + this.#wsServer.handleUpgrade(request, socket, head, (ws: WebSocket) => { + this.#wsServer.emit('connection', ws, request); }); }); } @@ -52,8 +52,8 @@ export class HttpServer { return await new Promise((resolve) => { // Bind to 'localhost' which resolves to IPv4 (127.0.0.1) or IPv6 (::1) // depending on system configuration and DNS resolution - this._server.listen(0, LOCALHOST, () => { - const addressInfo: AddressInfo | string | null = this._server.address(); + this.#server.listen(0, LOCALHOST, () => { + const addressInfo: AddressInfo | string | null = this.#server.address(); if (!addressInfo) { throw new Error('Server address is null - server may not be bound properly'); } @@ -61,27 +61,27 @@ export class HttpServer { throw new Error(`Server address is a pipe/socket path (${addressInfo}), expected an IP address`); } const formattedAddress: string = formatAddress(addressInfo); - this._listeningAddress = formattedAddress; + this.#listeningAddress = formattedAddress; // This MUST be printed to terminal so VS Code can auto-port forward - this._logger.writeLine(`Local proxy HttpServer listening at ws://${formattedAddress}`); + this.#logger.writeLine(`Local proxy HttpServer listening at ws://${formattedAddress}`); resolve(new URL(`ws://${formattedAddress}`)); }); }); } public get endpoint(): string { - if (this._listeningAddress === undefined) { + if (this.#listeningAddress === undefined) { throw new Error('HttpServer not listening yet'); } - return `ws://${this._listeningAddress}`; + return `ws://${this.#listeningAddress}`; } public get wsServer(): WebSocketServer { - return this._wsServer; + return this.#wsServer; } public [Symbol.dispose](): void { - this._wsServer.close(); - this._server.close(); + this.#wsServer.close(); + this.#server.close(); } } diff --git a/apps/playwright-browser-tunnel/src/PlaywrightBrowserTunnel.ts b/apps/playwright-browser-tunnel/src/PlaywrightBrowserTunnel.ts index 1fa0627b531..ae57cc0b620 100644 --- a/apps/playwright-browser-tunnel/src/PlaywrightBrowserTunnel.ts +++ b/apps/playwright-browser-tunnel/src/PlaywrightBrowserTunnel.ts @@ -88,20 +88,20 @@ interface IBrowserServerProxy { * @beta */ export class PlaywrightTunnel { - private readonly _terminal: ITerminal; - private readonly _onStatusChange: (status: TunnelStatus) => void; - private readonly _onBeforeLaunch?: (handshake: IHandshake) => Promise | boolean; - private readonly _playwrightBrowsersInstalled: Set = new Set(); - private readonly _wsEndpoint: string | undefined; - private readonly _listenPort: number | undefined; - private readonly _playwrightInstallPath: string; - private _status: TunnelStatus = 'stopped'; - private _initWsPromise?: Promise; - private _keepRunning: boolean = false; - private _ws?: WebSocket; - private _mode: TunnelMode; - private _pendingConnectionAttempt?: Promise; - private _pollInterval?: NodeJS.Timeout; + readonly #terminal: ITerminal; + readonly #onStatusChange: (status: TunnelStatus) => void; + readonly #onBeforeLaunch?: (handshake: IHandshake) => Promise | boolean; + readonly #playwrightBrowsersInstalled: Set = new Set(); + readonly #wsEndpoint: string | undefined; + readonly #listenPort: number | undefined; + readonly #playwrightInstallPath: string; + #status: TunnelStatus = 'stopped'; + #initWsPromise?: Promise; + #keepRunning: boolean = false; + #ws?: WebSocket; + #mode: TunnelMode; + #pendingConnectionAttempt?: Promise; + #pollInterval?: NodeJS.Timeout; public constructor(options: IPlaywrightTunnelOptions) { const { mode, terminal, onStatusChange, playwrightInstallPath, onBeforeLaunch } = options; @@ -111,55 +111,54 @@ export class PlaywrightTunnel { if (!options.wsEndpoint) { throw new Error('wsEndpoint is required for poll-connection mode'); } - this._wsEndpoint = options.wsEndpoint; - this._listenPort = undefined; + this.#wsEndpoint = options.wsEndpoint; + this.#listenPort = undefined; break; case 'wait-for-incoming-connection': if (options.listenPort === undefined) { throw new Error('listenPort is required for wait-for-incoming-connection mode'); } - this._wsEndpoint = undefined; - this._listenPort = options.listenPort; + this.#wsEndpoint = undefined; + this.#listenPort = options.listenPort; break; default: throw new Error(`Invalid mode: ${mode}`); } - this._mode = mode; - this._terminal = terminal; - this._onStatusChange = onStatusChange; - this._onBeforeLaunch = onBeforeLaunch; - this._playwrightInstallPath = playwrightInstallPath; + this.#mode = mode; + this.#terminal = terminal; + this.#onStatusChange = onStatusChange; + this.#onBeforeLaunch = onBeforeLaunch; + this.#playwrightInstallPath = playwrightInstallPath; } public get status(): TunnelStatus { - return this._status; + return this.#status; } - // eslint-disable-next-line @typescript-eslint/naming-convention - private set status(newStatus: TunnelStatus) { - this._status = newStatus; - this._onStatusChange(newStatus); + #setStatus(newStatus: TunnelStatus): void { + this.#status = newStatus; + this.#onStatusChange(newStatus); } public async waitForCloseAsync(): Promise { - const terminal: ITerminal = this._terminal; - const initWsPromise: Promise | undefined = this._initWsPromise; + const terminal: ITerminal = this.#terminal; + const initWsPromise: Promise | undefined = this.#initWsPromise; if (initWsPromise) { const ws: WebSocket = await initWsPromise; await once(ws, 'close'); terminal.writeDebugLine('WebSocket connection closed. resolving init promise.'); - this._initWsPromise = undefined; + this.#initWsPromise = undefined; } } public async startAsync(options: { keepRunning?: boolean } = {}): Promise { - this._keepRunning = options.keepRunning ?? true; - const terminal: ITerminal = this._terminal; - terminal.writeLine(`keepRunning: ${this._keepRunning}`); - while (this._keepRunning) { - if (!this._initWsPromise) { - this._initWsPromise = this._initPlaywrightBrowserTunnelAsync(); + this.#keepRunning = options.keepRunning ?? true; + const terminal: ITerminal = this.#terminal; + terminal.writeLine(`keepRunning: ${this.#keepRunning}`); + while (this.#keepRunning) { + if (!this.#initWsPromise) { + this.#initWsPromise = this.#initPlaywrightBrowserTunnelAsync(); } else { terminal.writeLine(`Tunnel is already running with status: ${this.status}`); } @@ -168,39 +167,39 @@ export class PlaywrightTunnel { } public async stopAsync(): Promise { - this._keepRunning = false; - if (this._pollInterval) { - clearInterval(this._pollInterval); - this._pollInterval = undefined; + this.#keepRunning = false; + if (this.#pollInterval) { + clearInterval(this.#pollInterval); + this.#pollInterval = undefined; } - await this._initWsPromise?.finally(() => { - this._ws?.close(WebSocketCloseCode.NORMAL_CLOSURE, 'Tunnel stopped'); + await this.#initWsPromise?.finally(() => { + this.#ws?.close(WebSocketCloseCode.NORMAL_CLOSURE, 'Tunnel stopped'); }); } public async [Symbol.asyncDispose](): Promise { - this._terminal.writeLine('Disposing WebSocket connection.'); + this.#terminal.writeLine('Disposing WebSocket connection.'); await this.stopAsync(); } public async cleanTempFilesAsync(): Promise { - const tmpPath: string = this._playwrightInstallPath; - this._terminal.writeLine(`Cleaning up temporary files in ${tmpPath}`); + const tmpPath: string = this.#playwrightInstallPath; + this.#terminal.writeLine(`Cleaning up temporary files in ${tmpPath}`); try { await FileSystem.ensureEmptyFolderAsync(tmpPath); - this._terminal.writeLine(`Temporary files cleaned up.`); + this.#terminal.writeLine(`Temporary files cleaned up.`); } catch (error) { - this._terminal.writeLine(`Failed to clean up temporary files: ${getNormalizedErrorString(error)}`); + this.#terminal.writeLine(`Failed to clean up temporary files: ${getNormalizedErrorString(error)}`); } } // TODO: We should implement an uninstall command to remove installed Playwright browsers // public async uninstallPlaywrightBrowsersAsync(): Promise {} - private async _runCommandAsync(command: string, args: string[]): Promise { - const tmpPath: string = this._playwrightInstallPath; + async #runCommandAsync(command: string, args: string[]): Promise { + const tmpPath: string = this.#playwrightInstallPath; await FileSystem.ensureFolderAsync(tmpPath); - this._terminal.writeLine(`Running command: ${command} ${args.join(' ')} in ${tmpPath}`); + this.#terminal.writeLine(`Running command: ${command} ${args.join(' ')} in ${tmpPath}`); const cp: ChildProcess = Executable.spawn(command, args, { stdio: [ @@ -213,13 +212,13 @@ export class PlaywrightTunnel { cp.stdout?.pipe( new TerminalStreamWritable({ - terminal: this._terminal, + terminal: this.#terminal, severity: TerminalProviderSeverity.log }) ); cp.stderr?.pipe( new TerminalStreamWritable({ - terminal: this._terminal, + terminal: this.#terminal, severity: TerminalProviderSeverity.error }) ); @@ -227,38 +226,38 @@ export class PlaywrightTunnel { await Executable.waitForExitAsync(cp, { throwOnNonZeroExitCode: true, throwOnSignal: true }); } - private async _installPlaywrightCoreAsync({ + async #installPlaywrightCoreAsync({ playwrightVersion }: Pick): Promise { - this._terminal.writeLine(`Installing playwright-core version ${playwrightVersion}`); - await this._runCommandAsync('npm', [ + this.#terminal.writeLine(`Installing playwright-core version ${playwrightVersion}`); + await this.#runCommandAsync('npm', [ 'install', `playwright-core-${playwrightVersion}@npm:playwright-core@${playwrightVersion}` ]); } - private async _installPlaywrightBrowsersAsync({ + async #installPlaywrightBrowsersAsync({ playwrightVersion, browserName }: Pick): Promise { - await this._installPlaywrightCoreAsync({ playwrightVersion }); - this._terminal.writeLine(`Executing playwright-core version ${playwrightVersion}`); - await this._runCommandAsync('node', [ + await this.#installPlaywrightCoreAsync({ playwrightVersion }); + this.#terminal.writeLine(`Executing playwright-core version ${playwrightVersion}`); + await this.#runCommandAsync('node', [ `node_modules/playwright-core-${playwrightVersion}/cli.js`, 'install', browserName ]); } - private async _tryConnectAsync(): Promise { - const wsEndpoint: string | undefined = this._wsEndpoint; + async #tryConnectAsync(): Promise { + const wsEndpoint: string | undefined = this.#wsEndpoint; if (!wsEndpoint) { throw new Error('WebSocket endpoint is not defined'); } return await new Promise((resolve, reject) => { const ws: WebSocket = new WebSocket(wsEndpoint); ws.on('open', () => { - this._terminal.writeLine(`WebSocket connection opened`); + this.#terminal.writeLine(`WebSocket connection opened`); resolve(ws); }); ws.once('error', (error) => { @@ -269,49 +268,49 @@ export class PlaywrightTunnel { // TODO: Only supporting one test at a time. // Need to support multiple simultaneous connections for parallel tests. - private async _pollConnectionAsync(): Promise { - this._terminal.writeLine(`Waiting for WebSocket connection`); + async #pollConnectionAsync(): Promise { + this.#terminal.writeLine(`Waiting for WebSocket connection`); return await new Promise((resolve, reject) => { - this._pollInterval = setInterval(() => { - if (this._pendingConnectionAttempt) { + this.#pollInterval = setInterval(() => { + if (this.#pendingConnectionAttempt) { return; // Skip if a connection attempt is already in progress } - const connectionPromise: Promise = this._tryConnectAsync(); - this._pendingConnectionAttempt = connectionPromise; + const connectionPromise: Promise = this.#tryConnectAsync(); + this.#pendingConnectionAttempt = connectionPromise; connectionPromise .then((ws: WebSocket) => { - clearInterval(this._pollInterval); - this._pollInterval = undefined; + clearInterval(this.#pollInterval); + this.#pollInterval = undefined; ws.removeAllListeners(); - this._pendingConnectionAttempt = undefined; + this.#pendingConnectionAttempt = undefined; resolve(ws); }) .catch(() => { // no-op - will retry on next interval - this._pendingConnectionAttempt = undefined; + this.#pendingConnectionAttempt = undefined; }); }, 500); }); } - private async _waitForIncomingConnectionAsync(): Promise { - this._terminal.writeLine('Waiting for incoming WebSocket connection'); + async #waitForIncomingConnectionAsync(): Promise { + this.#terminal.writeLine('Waiting for incoming WebSocket connection'); return await new Promise((resolve, reject) => { - const server: WebSocketServer = new WebSocket.Server({ port: this._listenPort }); + const server: WebSocketServer = new WebSocket.Server({ port: this.#listenPort }); const cleanup = (): void => { server.removeAllListeners(); }; server.once('connection', (ws) => { - this._terminal.writeLine('Incoming WebSocket connection established'); + this.#terminal.writeLine('Incoming WebSocket connection established'); // Stop listening immediately so the port is released cleanup(); server.close((closeError?: Error) => { if (closeError) { - this._terminal.writeLine( + this.#terminal.writeLine( `Failed to close WebSocket server: ${ closeError instanceof Error ? closeError.message : closeError }` @@ -322,7 +321,7 @@ export class PlaywrightTunnel { }); server.once('error', (error) => { - this._terminal.writeLine(`WebSocket server error: ${getNormalizedErrorString(error)}`); + this.#terminal.writeLine(`WebSocket server error: ${getNormalizedErrorString(error)}`); cleanup(); // Try to close (best-effort), then reject @@ -334,30 +333,30 @@ export class PlaywrightTunnel { // TODO: If a user runs this for the first time, `this._playwrightBrowsersInstalled` will be empty // and it will try to install the browsers every time. We should persist this information. Maybe a cache file with text per // machine instance? - private async _setupPlaywrightAsync({ + async #setupPlaywrightAsync({ playwrightVersion, browserName }: Pick): Promise { const browserKey: string = `${playwrightVersion}-${browserName}`; - this._terminal.writeLine(`Checking for installed playwright browsers. Installed browsers: ${browserKey}`); - if (!this._playwrightBrowsersInstalled.has(browserKey)) { - this._terminal.writeLine( + this.#terminal.writeLine(`Checking for installed playwright browsers. Installed browsers: ${browserKey}`); + if (!this.#playwrightBrowsersInstalled.has(browserKey)) { + this.#terminal.writeLine( `Playwright browser not found. Installing playwright-core version ${playwrightVersion}` ); - await this._installPlaywrightBrowsersAsync({ playwrightVersion, browserName }); - this._playwrightBrowsersInstalled.add(browserKey); + await this.#installPlaywrightBrowsersAsync({ playwrightVersion, browserName }); + this.#playwrightBrowsersInstalled.add(browserKey); } - this._terminal.writeLine(`Using playwright-core version ${playwrightVersion} for browser server`); - return await import(`${this._playwrightInstallPath}/node_modules/playwright-core-${playwrightVersion}`); + this.#terminal.writeLine(`Using playwright-core version ${playwrightVersion} for browser server`); + return await import(`${this.#playwrightInstallPath}/node_modules/playwright-core-${playwrightVersion}`); } - private async _getPlaywrightBrowserServerProxyAsync({ + async #getPlaywrightBrowserServerProxyAsync({ browserName, playwrightVersion, launchOptions }: Pick): Promise { - const terminal: ITerminal = this._terminal; + const terminal: ITerminal = this.#terminal; // Validate launch options against security allowlist terminal.writeLine('Validating launch options against security allowlist...'); @@ -385,7 +384,7 @@ export class PlaywrightTunnel { `Launch options after validation: ${JSON.stringify(logOptions)} (headless: false enforced)` ); - const playwright: typeof import('playwright-core') = await this._setupPlaywrightAsync({ + const playwright: typeof import('playwright-core') = await this.#setupPlaywrightAsync({ playwrightVersion, browserName }); @@ -410,7 +409,7 @@ export class PlaywrightTunnel { }; } - private _validateHandshake(rawHandshake: unknown): IHandshake { + #validateHandshake(rawHandshake: unknown): IHandshake { if ( typeof rawHandshake !== 'object' || rawHandshake === null || @@ -447,10 +446,10 @@ export class PlaywrightTunnel { } // ws1 is the tunnel websocket, ws2 is the browser server websocket - private async _setupForwardingAsync(ws1: WebSocket, ws2: WebSocket): Promise { - this._terminal.writeLine('Setting up message forwarding between ws1 and ws2'); - this._terminal.writeLine(` ws1 (tunnel) readyState: ${getWebSocketReadyStateString(ws1.readyState)}`); - this._terminal.writeLine(` ws2 (browser) readyState: ${getWebSocketReadyStateString(ws2.readyState)}`); + async #setupForwardingAsync(ws1: WebSocket, ws2: WebSocket): Promise { + this.#terminal.writeLine('Setting up message forwarding between ws1 and ws2'); + this.#terminal.writeLine(` ws1 (tunnel) readyState: ${getWebSocketReadyStateString(ws1.readyState)}`); + this.#terminal.writeLine(` ws2 (browser) readyState: ${getWebSocketReadyStateString(ws2.readyState)}`); const messageCount: { ws1ToWs2: number; ws2ToWs1: number } = { ws1ToWs2: 0, ws2ToWs1: 0 }; @@ -459,7 +458,7 @@ export class PlaywrightTunnel { if (ws2.readyState === WebSocket.OPEN) { ws2.send(data); } else { - this._terminal.writeLine( + this.#terminal.writeLine( `ws2 not open (state: ${getWebSocketReadyStateString(ws2.readyState)}). Dropping message #${messageCount.ws1ToWs2}` ); } @@ -469,7 +468,7 @@ export class PlaywrightTunnel { if (ws1.readyState === WebSocket.OPEN) { ws1.send(data); } else { - this._terminal.writeLine( + this.#terminal.writeLine( `ws1 not open (state: ${getWebSocketReadyStateString(ws1.readyState)}). Dropping message #${messageCount.ws2ToWs1}` ); } @@ -478,39 +477,39 @@ export class PlaywrightTunnel { ws1.once('close', (code: number, reason: Buffer) => { const reasonStr: string = reason.toString() || 'no reason provided'; const codeDescription: string = getWebSocketCloseReason(code); - this._terminal.writeLine( + this.#terminal.writeLine( `ws1 (tunnel) closed - code: ${code} (${codeDescription}), reason: ${reasonStr}` ); - this._terminal.writeLine( + this.#terminal.writeLine( ` Messages forwarded: ws1->ws2: ${messageCount.ws1ToWs2}, ws2->ws1: ${messageCount.ws2ToWs1}` ); if (ws2.readyState === WebSocket.OPEN) { - this._terminal.writeLine(' Closing ws2 (browser) in response'); + this.#terminal.writeLine(' Closing ws2 (browser) in response'); ws2.close(WebSocketCloseCode.NORMAL_CLOSURE, 'Tunnel closed'); } }); ws2.once('close', (code: number, reason: Buffer) => { const reasonStr: string = reason.toString() || 'no reason provided'; const codeDescription: string = getWebSocketCloseReason(code); - this._terminal.writeLine( + this.#terminal.writeLine( `ws2 (browser) closed - code: ${code} (${codeDescription}), reason: ${reasonStr}` ); - this._terminal.writeLine( + this.#terminal.writeLine( ` Messages forwarded: ws1->ws2: ${messageCount.ws1ToWs2}, ws2->ws1: ${messageCount.ws2ToWs1}` ); if (ws1.readyState === WebSocket.OPEN) { - this._terminal.writeLine(' Closing ws1 (tunnel) in response'); + this.#terminal.writeLine(' Closing ws1 (tunnel) in response'); ws1.close(WebSocketCloseCode.NORMAL_CLOSURE, 'Browser closed'); } }); ws1.once('error', (error) => { - this._terminal.writeErrorLine(`ws1 (tunnel) WebSocket error: ${getNormalizedErrorString(error)}`); - this._terminal.writeErrorLine(` ws1 readyState: ${getWebSocketReadyStateString(ws1.readyState)}`); + this.#terminal.writeErrorLine(`ws1 (tunnel) WebSocket error: ${getNormalizedErrorString(error)}`); + this.#terminal.writeErrorLine(` ws1 readyState: ${getWebSocketReadyStateString(ws1.readyState)}`); }); ws2.once('error', (error) => { - this._terminal.writeErrorLine(`ws2 (browser) WebSocket error: ${getNormalizedErrorString(error)}`); - this._terminal.writeErrorLine(` ws2 readyState: ${getWebSocketReadyStateString(ws2.readyState)}`); + this.#terminal.writeErrorLine(`ws2 (browser) WebSocket error: ${getNormalizedErrorString(error)}`); + this.#terminal.writeErrorLine(` ws2 readyState: ${getWebSocketReadyStateString(ws2.readyState)}`); }); } @@ -519,57 +518,57 @@ export class PlaywrightTunnel { * and setting up the browser server. * Returns when the handshake is complete and the browser server is running. */ - private async _initPlaywrightBrowserTunnelAsync(): Promise { + async #initPlaywrightBrowserTunnelAsync(): Promise { let handshake: IHandshake | undefined = undefined; let client: WebSocket | undefined = undefined; let browserServer: BrowserServer | undefined = undefined; - this.status = 'waiting-for-connection'; + this.#setStatus('waiting-for-connection'); const ws: WebSocket = - this._mode === 'poll-connection' - ? await this._pollConnectionAsync() - : await this._waitForIncomingConnectionAsync(); + this.#mode === 'poll-connection' + ? await this.#pollConnectionAsync() + : await this.#waitForIncomingConnectionAsync(); ws.on('open', () => { - this._terminal.writeLine(`WebSocket connection established`); + this.#terminal.writeLine(`WebSocket connection established`); handshake = undefined; }); ws.on('error', (error) => { - this._terminal.writeLine(`WebSocket error occurred: ${getNormalizedErrorString(error)}`); + this.#terminal.writeLine(`WebSocket error occurred: ${getNormalizedErrorString(error)}`); }); ws.on('close', async (code: number, reason: Buffer) => { const reasonStr: string = reason.toString() || 'no reason provided'; const codeDescription: string = getWebSocketCloseReason(code); - this._initWsPromise = undefined; - this.status = 'stopped'; - this._terminal.writeLine( + this.#initWsPromise = undefined; + this.#setStatus('stopped'); + this.#terminal.writeLine( `WebSocket connection closed - code: ${code} (${codeDescription}), reason: ${reasonStr}` ); - this._terminal.writeLine(` handshake received: ${handshake !== undefined}`); - this._terminal.writeLine(` browserServer active: ${browserServer !== undefined}`); + this.#terminal.writeLine(` handshake received: ${handshake !== undefined}`); + this.#terminal.writeLine(` browserServer active: ${browserServer !== undefined}`); if (browserServer) { - this._terminal.writeLine(' Closing browser server...'); + this.#terminal.writeLine(' Closing browser server...'); await browserServer.close(); - this._terminal.writeLine(' Browser server closed'); + this.#terminal.writeLine(' Browser server closed'); } }); return await new Promise((resolve, reject) => { const onMessageHandler = async (data: RawData): Promise => { - const terminal: ITerminal = this._terminal; + const terminal: ITerminal = this.#terminal; if (!handshake) { try { const rawHandshakeString: string = data.toString(); const rawHandshake: unknown = JSON.parse(rawHandshakeString); terminal.writeLine(`Received handshake: ${rawHandshakeString}`); - handshake = this._validateHandshake(rawHandshake); + handshake = this.#validateHandshake(rawHandshake); // Call the onBeforeLaunch callback if provided - if (this._onBeforeLaunch) { + if (this.#onBeforeLaunch) { terminal.writeLine('Requesting user approval before launching browser server...'); - const shouldProceed: boolean = await this._onBeforeLaunch(handshake); + const shouldProceed: boolean = await this.#onBeforeLaunch(handshake); if (!shouldProceed) { terminal.writeLine('Browser server launch cancelled by user.'); ws.off('message', onMessageHandler); @@ -580,9 +579,9 @@ export class PlaywrightTunnel { terminal.writeLine('User approved browser server launch.'); } - this.status = 'setting-up-browser-server'; + this.#setStatus('setting-up-browser-server'); const browserServerProxy: IBrowserServerProxy = - await this._getPlaywrightBrowserServerProxyAsync(handshake); + await this.#getPlaywrightBrowserServerProxyAsync(handshake); client = browserServerProxy.client; browserServer = browserServerProxy.browserServer; @@ -600,7 +599,7 @@ export class PlaywrightTunnel { terminal.writeDebugLine('Warning: Browser server process handle not available for monitoring'); } - this.status = 'browser-server-running'; + this.#setStatus('browser-server-running'); // Send ack so that the counterpart also knows to start forwarding messages. // NOTE: The 1-second delay is an intentional workaround. In the current @@ -616,14 +615,14 @@ export class PlaywrightTunnel { await Async.sleepAsync(2000); ws.send(JSON.stringify({ action: 'handshakeAck' })); - await this._setupForwardingAsync(ws, client); + await this.#setupForwardingAsync(ws, client); // Clean up message handler after successful handshake ws.off('message', onMessageHandler); resolve(ws); } catch (error) { terminal.writeLine(`Error processing handshake: ${error}`); - this.status = 'error'; + this.#setStatus('error'); // Cleanup and close connection on error ws.off('message', onMessageHandler); diff --git a/apps/rundown/src/Rundown.ts b/apps/rundown/src/Rundown.ts index 9a4dd720817..7c1856ee9d0 100644 --- a/apps/rundown/src/Rundown.ts +++ b/apps/rundown/src/Rundown.ts @@ -12,7 +12,7 @@ import type { IpcMessage } from './LauncherTypes'; export class Rundown { // Map from required path --> caller path - private _importedModuleMap: Map = new Map(); + #importedModuleMap: Map = new Map(); public async invokeAsync( scriptPath: string, @@ -34,7 +34,7 @@ export class Rundown { // ["path/to/launcher.js", "path/to/target-script.js", "first-target-arg"] const nodeArgs: string[] = [path.join(__dirname, 'launcher.js'), absoluteScriptPath, ...expandedArgs]; - await this._spawnLauncherAsync(nodeArgs, quiet, ignoreExitCode); + await this.#spawnLauncherAsync(nodeArgs, quiet, ignoreExitCode); if (!quiet) { console.log(); @@ -46,7 +46,7 @@ export class Rundown { console.log('Writing report file: ' + reportPath); const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); - const importedPaths: string[] = [...this._importedModuleMap.keys()]; + const importedPaths: string[] = [...this.#importedModuleMap.keys()]; const importedPackageFolders: Set = new Set(); for (const importedPath of importedPaths) { @@ -75,7 +75,7 @@ export class Rundown { const reportPath: string = 'rundown-inspect.log'; console.log('Writing report file: ' + reportPath); - const importedPaths: string[] = [...this._importedModuleMap.keys()]; + const importedPaths: string[] = [...this.#importedModuleMap.keys()]; importedPaths.sort(); let data: string = ''; @@ -87,7 +87,7 @@ export class Rundown { let current: string = importedPath; const visited: Set = new Set(); for (;;) { - const callerPath: string | undefined = this._importedModuleMap.get(current); + const callerPath: string | undefined = this.#importedModuleMap.get(current); if (!callerPath) { break; } @@ -107,7 +107,7 @@ export class Rundown { FileSystem.writeFile(reportPath, data); } - private async _spawnLauncherAsync( + async #spawnLauncherAsync( nodeArgs: string[], quiet: boolean, ignoreExitCode: boolean @@ -122,7 +122,7 @@ export class Rundown { switch (message.id) { case 'trace': for (const record of message.records) { - this._importedModuleMap.set(record.importedModule, record.callingModule); + this.#importedModuleMap.set(record.importedModule, record.callingModule); } break; case 'done': diff --git a/apps/rundown/src/cli/InspectAction.ts b/apps/rundown/src/cli/InspectAction.ts index 9c14086c55e..4d31e3c402c 100644 --- a/apps/rundown/src/cli/InspectAction.ts +++ b/apps/rundown/src/cli/InspectAction.ts @@ -7,7 +7,7 @@ import { BaseReportAction } from './BaseReportAction'; import { Rundown } from '../Rundown'; export class InspectAction extends BaseReportAction { - private readonly _traceParameter: CommandLineFlagParameter; + readonly #traceParameter: CommandLineFlagParameter; public constructor() { super({ @@ -18,7 +18,7 @@ export class InspectAction extends BaseReportAction { ' to inspect performance regressions.' }); - this._traceParameter = this.defineFlagParameter({ + this.#traceParameter = this.defineFlagParameter({ parameterLongName: '--trace-imports', parameterShortName: '-t', description: 'Reports the call chain for each module path, showing how it was imported' @@ -33,6 +33,6 @@ export class InspectAction extends BaseReportAction { this.quietParameter.value, this.ignoreExitCodeParameter.value ); - rundown.writeInspectReport(this._traceParameter.value); + rundown.writeInspectReport(this.#traceParameter.value); } } diff --git a/apps/rundown/src/launcher.ts b/apps/rundown/src/launcher.ts index 63738d1bea3..fc1c14ff04c 100644 --- a/apps/rundown/src/launcher.ts +++ b/apps/rundown/src/launcher.ts @@ -14,9 +14,9 @@ class Launcher { public action: LauncherAction = LauncherAction.Inspect; public targetScriptPathArg: string = ''; public reportPath: string = ''; - private _importedModules: Set = new Set(); - private _importedModulePaths: Set = new Set(); - private _ipcTraceRecordsBatch: IIpcTraceRecord[] = []; + #importedModules: Set = new Set(); + #importedModulePaths: Set = new Set(); + #ipcTraceRecordsBatch: IIpcTraceRecord[] = []; public transformArgs(argv: ReadonlyArray): string[] { let nodeArg: string; @@ -31,10 +31,10 @@ class Launcher { return [nodeArg, this.targetScriptPathArg, ...remainderArgs]; } - private _sendIpcTraceBatch(): void { - if (this._ipcTraceRecordsBatch.length > 0) { - const batch: IIpcTraceRecord[] = [...this._ipcTraceRecordsBatch]; - this._ipcTraceRecordsBatch.length = 0; + #sendIpcTraceBatch(): void { + if (this.#ipcTraceRecordsBatch.length > 0) { + const batch: IIpcTraceRecord[] = [...this.#ipcTraceRecordsBatch]; + this.#ipcTraceRecordsBatch.length = 0; process.send!({ id: 'trace', @@ -46,10 +46,10 @@ class Launcher { public installHook(): void { const realRequire: typeof moduleApi.Module.prototype.require = moduleApi.Module.prototype.require; - const importedModules: Set = this._importedModules; // for closure - const importedModulePaths: Set = this._importedModulePaths; // for closure - const ipcTraceRecordsBatch: IIpcTraceRecord[] = this._ipcTraceRecordsBatch; // for closure - const sendIpcTraceBatch: () => void = this._sendIpcTraceBatch.bind(this); // for closure + const importedModules: Set = this.#importedModules; // for closure + const importedModulePaths: Set = this.#importedModulePaths; // for closure + const ipcTraceRecordsBatch: IIpcTraceRecord[] = this.#ipcTraceRecordsBatch; // for closure + const sendIpcTraceBatch: () => void = this.#sendIpcTraceBatch.bind(this); // for closure function hookedRequire(this: NodeModule, moduleName: string): unknown { // NOTE: The "this" pointer is the calling NodeModule, so we rely on closure @@ -100,7 +100,7 @@ class Launcher { _copyProperties(hookedRequire, realRequire); process.on('exit', () => { - this._sendIpcTraceBatch(); + this.#sendIpcTraceBatch(); process.send!({ id: 'done' } as IIpcDone); diff --git a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts index a760a847b0d..e0599ad3bb2 100644 --- a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts +++ b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts @@ -71,17 +71,17 @@ const _rushMcpJsonSchema: JsonSchema = JsonSchema.fromLoadedObject(rushMcpJsonSc const _rushMcpPluginSchemaObject: JsonSchema = JsonSchema.fromLoadedObject(rushMcpPluginSchemaObject); export class RushMcpPluginLoader { - private readonly _rushWorkspacePath: string; - private readonly _mcpServer: McpServer; + readonly #rushWorkspacePath: string; + readonly #mcpServer: McpServer; public constructor(rushWorkspacePath: string, mcpServer: McpServer) { - this._rushWorkspacePath = rushWorkspacePath; - this._mcpServer = mcpServer; + this.#rushWorkspacePath = rushWorkspacePath; + this.#mcpServer = mcpServer; } public async loadAsync(): Promise { const rushMcpFilePath: string = path.join( - this._rushWorkspacePath, + this.#rushWorkspacePath, 'common/config/rush-mcp/rush-mcp.json' ); @@ -90,7 +90,7 @@ export class RushMcpPluginLoader { } const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation({ - startingFolder: this._rushWorkspacePath + startingFolder: this.#rushWorkspacePath }); const jsonRushMcpConfig: IJsonRushMcpConfig = await JsonFile.loadAndValidateAsync( @@ -144,7 +144,7 @@ export class RushMcpPluginLoader { ); const mcpPluginSchema: JsonSchema = await JsonSchema.fromFile(mcpPluginSchemaFilePath); const rushMcpPluginOptionsFilePath: string = path.resolve( - this._rushWorkspacePath, + this.#rushWorkspacePath, `common/config/rush-mcp/${jsonManifest.pluginName}.json` ); // Example: /path/to/my-repo/common/config/rush-mcp/rush-mcp-example-plugin.json @@ -166,7 +166,7 @@ export class RushMcpPluginLoader { throw new Error(`Unable to load plugin entry point at ${fullEntryPointPath}:\n` + _formatError(e)); } - const session: RushMcpPluginSessionInternal = new RushMcpPluginSessionInternal(this._mcpServer); + const session: RushMcpPluginSessionInternal = new RushMcpPluginSessionInternal(this.#mcpServer); let plugin: IRushMcpPlugin; try { diff --git a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginSession.ts b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginSession.ts index aa46003a8cf..0599f96a82d 100644 --- a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginSession.ts +++ b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginSession.ts @@ -28,23 +28,23 @@ export abstract class RushMcpPluginSession { } export class RushMcpPluginSessionInternal extends RushMcpPluginSession { - private readonly _mcpServer: McpServer; + readonly #mcpServer: McpServer; public constructor(mcpServer: McpServer) { super(); - this._mcpServer = mcpServer; + this.#mcpServer = mcpServer; } public override registerTool(options: IRegisterToolOptions, tool: IRushMcpTool): void { if (options.description) { - this._mcpServer.tool( + this.#mcpServer.tool( options.toolName, options.description, tool.schema.shape, tool.executeAsync.bind(tool) ); } else { - this._mcpServer.tool(options.toolName, tool.schema.shape, tool.executeAsync.bind(tool)); + this.#mcpServer.tool(options.toolName, tool.schema.shape, tool.executeAsync.bind(tool)); } } } diff --git a/apps/rush-mcp-server/src/server.ts b/apps/rush-mcp-server/src/server.ts index 5eceb116012..dce4e06693d 100644 --- a/apps/rush-mcp-server/src/server.ts +++ b/apps/rush-mcp-server/src/server.ts @@ -14,9 +14,9 @@ import { import { RushMcpPluginLoader } from './pluginFramework/RushMcpPluginLoader'; export class RushMCPServer extends McpServer { - private _rushWorkspacePath: string; - private _tools: BaseTool[] = []; - private _pluginLoader: RushMcpPluginLoader; + #rushWorkspacePath: string; + #tools: BaseTool[] = []; + #pluginLoader: RushMcpPluginLoader; public constructor(rushWorkspacePath: string) { super({ @@ -24,29 +24,29 @@ export class RushMCPServer extends McpServer { version: '1.0.0' }); - this._rushWorkspacePath = rushWorkspacePath; - this._pluginLoader = new RushMcpPluginLoader(this._rushWorkspacePath, this); + this.#rushWorkspacePath = rushWorkspacePath; + this.#pluginLoader = new RushMcpPluginLoader(this.#rushWorkspacePath, this); } public async startAsync(): Promise { - this._initializeTools(); - this._registerTools(); + this.#initializeTools(); + this.#registerTools(); - await this._pluginLoader.loadAsync(); + await this.#pluginLoader.loadAsync(); } - private _initializeTools(): void { - this._tools.push(new RushConflictResolverTool()); - this._tools.push(new RushMigrateProjectTool(this._rushWorkspacePath)); - this._tools.push(new RushCommandValidatorTool()); - this._tools.push(new RushWorkspaceDetailsTool()); - this._tools.push(new RushProjectDetailsTool()); + #initializeTools(): void { + this.#tools.push(new RushConflictResolverTool()); + this.#tools.push(new RushMigrateProjectTool(this.#rushWorkspacePath)); + this.#tools.push(new RushCommandValidatorTool()); + this.#tools.push(new RushWorkspaceDetailsTool()); + this.#tools.push(new RushProjectDetailsTool()); } - private _registerTools(): void { - process.chdir(this._rushWorkspacePath); + #registerTools(): void { + process.chdir(this.#rushWorkspacePath); - for (const tool of this._tools) { + for (const tool of this.#tools) { tool.register(this); } } diff --git a/apps/rush-mcp-server/src/tools/base.tool.ts b/apps/rush-mcp-server/src/tools/base.tool.ts index dc9b4d67bdb..b491af3975d 100644 --- a/apps/rush-mcp-server/src/tools/base.tool.ts +++ b/apps/rush-mcp-server/src/tools/base.tool.ts @@ -28,10 +28,10 @@ export interface IBaseToolOptions { } export abstract class BaseTool { - private _options: IBaseToolOptions; + #options: IBaseToolOptions; protected constructor(options: IBaseToolOptions) { - this._options = options; + this.#options = options; } protected abstract executeAsync(...args: Parameters>): ReturnType>; @@ -39,7 +39,7 @@ export abstract class BaseTool { public register(server: McpServer): void { // TODO: remove ts-ignore // @ts-ignore - server.tool(this._options.name, this._options.description, this._options.schema, async (...args) => { + server.tool(this.#options.name, this.#options.description, this.#options.schema, async (...args) => { try { const result: CallToolResult = await this.executeAsync(...(args as Parameters>)); return result; diff --git a/apps/rush-mcp-server/src/tools/conflict-resolver.tool.ts b/apps/rush-mcp-server/src/tools/conflict-resolver.tool.ts index 3c9a085b9f4..6b57ac07515 100644 --- a/apps/rush-mcp-server/src/tools/conflict-resolver.tool.ts +++ b/apps/rush-mcp-server/src/tools/conflict-resolver.tool.ts @@ -22,10 +22,10 @@ export class RushConflictResolverTool extends BaseTool { }); } - private _tryGetSubspaceNameFromLockfilePath( + #tryGetSubspaceNameFromLockfilePath( lockfilePath: string, rushConfiguration: RushConfiguration - ): string | null { + ): string | null { // eslint-disable-line @rushstack/no-new-null -- The decoupled ESLint plugin does not recognize native private methods yet. for (const subspace of rushConfiguration.subspaces) { const folderPath: string = subspace.getSubspaceConfigFolderPath(); if (lockfilePath.startsWith(folderPath)) { @@ -37,7 +37,7 @@ export class RushConflictResolverTool extends BaseTool { public async executeAsync({ lockfilePath }: { lockfilePath: string }): Promise { const rushConfiguration: RushConfiguration = await getRushConfiguration(); - const subspaceName: string | null = this._tryGetSubspaceNameFromLockfilePath( + const subspaceName: string | null = this.#tryGetSubspaceNameFromLockfilePath( lockfilePath, rushConfiguration ); diff --git a/apps/rush-mcp-server/src/tools/migrate-project.tool.ts b/apps/rush-mcp-server/src/tools/migrate-project.tool.ts index d4069a1d79c..f36414f2795 100644 --- a/apps/rush-mcp-server/src/tools/migrate-project.tool.ts +++ b/apps/rush-mcp-server/src/tools/migrate-project.tool.ts @@ -15,7 +15,7 @@ import { BaseTool, type CallToolResult } from './base.tool'; import { getRushConfiguration } from '../utilities/common'; export class RushMigrateProjectTool extends BaseTool { - private _rushWorkspacePath: string; + #rushWorkspacePath: string; public constructor(rushWorkspacePath: string) { super({ @@ -28,10 +28,10 @@ export class RushMigrateProjectTool extends BaseTool { } }); - this._rushWorkspacePath = rushWorkspacePath; + this.#rushWorkspacePath = rushWorkspacePath; } - private async _modifyAndSaveSubspaceJsonFileAsync( + async #modifyAndSaveSubspaceJsonFileAsync( rushConfiguration: RushConfiguration, cb: (subspaceNames: string[]) => Promise | string[] ): Promise { @@ -47,7 +47,7 @@ export class RushMigrateProjectTool extends BaseTool { }); } - private async _modifyAndSaveRushConfigurationAsync( + async #modifyAndSaveRushConfigurationAsync( rushConfiguration: RushConfiguration, cb: ( projects: IRushConfigurationProjectJson[] @@ -81,7 +81,7 @@ export class RushMigrateProjectTool extends BaseTool { }; } - const rootPath: string = this._rushWorkspacePath; + const rootPath: string = this.#rushWorkspacePath; const sourceProjectSubspaceName: string = project.subspace.subspaceName; const sourceProjectPath: string = project.projectFolder; const destinationPath: string = path.resolve(rootPath, targetProjectPath); @@ -100,7 +100,7 @@ export class RushMigrateProjectTool extends BaseTool { }); // 3. Update rush configuration - await this._modifyAndSaveRushConfigurationAsync(rushConfiguration, (projects) => { + await this.#modifyAndSaveRushConfigurationAsync(rushConfiguration, (projects) => { const projectIndex: number = projects.findIndex(({ packageName }) => packageName === projectName); projects[projectIndex] = { ...projects[projectIndex], @@ -111,7 +111,7 @@ export class RushMigrateProjectTool extends BaseTool { }); // 4. Update `subspaces.json` - await this._modifyAndSaveSubspaceJsonFileAsync(rushConfiguration, (subspaceNames) => { + await this.#modifyAndSaveSubspaceJsonFileAsync(rushConfiguration, (subspaceNames) => { if (subspacehasOnlyOneProject) { subspaceNames.splice(subspaceNames.indexOf(sourceProjectSubspaceName), 1); } diff --git a/apps/rush-mcp-server/src/tools/workspace-details.ts b/apps/rush-mcp-server/src/tools/workspace-details.ts index da93f38abb7..68a2a256eda 100644 --- a/apps/rush-mcp-server/src/tools/workspace-details.ts +++ b/apps/rush-mcp-server/src/tools/workspace-details.ts @@ -25,13 +25,13 @@ export class RushWorkspaceDetailsTool extends BaseTool { content: [ { type: 'text', - text: this._getWorkspaceDetailsPrompt(rushConfiguration, projects) + text: this.#getWorkspaceDetailsPrompt(rushConfiguration, projects) } ] }; } - private _getWorkspaceDetailsPrompt( + #getWorkspaceDetailsPrompt( rushConfiguration: RushConfiguration, projects: RushConfigurationProject[] ): string { @@ -54,11 +54,11 @@ PROJECT LEVEL information is separated by tags. Ea This data is very important. Use it to analyze the workspace and understand the project graph. The user cannot see this data, so don't reference it directly. It is read-only information to help you understand the workspace. -${this._getRobotReadableWorkspaceDetails(rushConfiguration.rushConfigurationJson, projects)} +${this.#getRobotReadableWorkspaceDetails(rushConfiguration.rushConfigurationJson, projects)} `.trim(); } - private _getRobotReadableWorkspaceDetails( + #getRobotReadableWorkspaceDetails( rushConfiguration: IRushConfigurationJson, projects: RushConfigurationProject[] ): string { diff --git a/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts b/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts index 68ac598e6cd..4e1ecdcb6df 100644 --- a/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts +++ b/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts @@ -15,7 +15,7 @@ interface IAnsiState { } export class AnsiSgrParser { - private readonly _state: IAnsiState = { + readonly #state: IAnsiState = { bold: false, underline: false, inverse: false, @@ -30,7 +30,7 @@ export class AnsiSgrParser { const pushSegmentIfText = (text: string): void => { if (!text) return; - const style: string = this._ansiStateToStyle(this._state); + const style: string = this.#ansiStateToStyle(this.#state); segments.push({ text, style }); }; @@ -52,7 +52,7 @@ export class AnsiSgrParser { const sequenceEnd: number = escapeIndex + 2 + suffixMatch[0].length; const seq: string = input.slice(escapeIndex, sequenceEnd); try { - this._applySgr(this._parseSgrParams(seq)); + this.#applySgr(this.#parseSgrParams(seq)); } catch { // Ignore malformed control sequences. } @@ -68,7 +68,7 @@ export class AnsiSgrParser { return segments; } - private _parseSgrParams(seq: string): number[] { + #parseSgrParams(seq: string): number[] { let s: string = seq; if (s.startsWith('\u001b[')) { s = s.slice(2); @@ -80,43 +80,43 @@ export class AnsiSgrParser { return s.split(';').map((p) => Number(p || 0)); } - private _applySgr(params: number[]): void { + #applySgr(params: number[]): void { if (!params || !params.length) params = [0]; for (const p of params) { if (p === 0) { - this._state.bold = false; - this._state.underline = false; - this._state.inverse = false; - this._state.fg = undefined; - this._state.bg = undefined; + this.#state.bold = false; + this.#state.underline = false; + this.#state.inverse = false; + this.#state.fg = undefined; + this.#state.bg = undefined; } else if (p === 1) { - this._state.bold = true; + this.#state.bold = true; } else if (p === 4) { - this._state.underline = true; + this.#state.underline = true; } else if (p === 7) { - this._state.inverse = true; + this.#state.inverse = true; } else if (p === 22) { - this._state.bold = false; + this.#state.bold = false; } else if (p === 24) { - this._state.underline = false; + this.#state.underline = false; } else if (p >= 30 && p <= 37) { - this._state.fg = this._sgrColorToCss(p - 30, false); + this.#state.fg = this.#sgrColorToCss(p - 30, false); } else if (p === 39) { - this._state.fg = undefined; + this.#state.fg = undefined; } else if (p >= 40 && p <= 47) { - this._state.bg = this._sgrColorToCss(p - 40, false); + this.#state.bg = this.#sgrColorToCss(p - 40, false); } else if (p === 49) { - this._state.bg = undefined; + this.#state.bg = undefined; } else if (p >= 90 && p <= 97) { - this._state.fg = this._sgrColorToCss(p - 90, true); + this.#state.fg = this.#sgrColorToCss(p - 90, true); } else if (p >= 100 && p <= 107) { - this._state.bg = this._sgrColorToCss(p - 100, true); + this.#state.bg = this.#sgrColorToCss(p - 100, true); } } } - private _sgrColorToCss(idx: number, bright: boolean): string | undefined { + #sgrColorToCss(idx: number, bright: boolean): string | undefined { const base: string[] = ['#000000', '#a00', '#0a0', '#aa0', '#00a', '#a0a', '#0aa', '#ddd']; const brightMap: string[] = [ '#555', @@ -131,7 +131,7 @@ export class AnsiSgrParser { return bright ? brightMap[idx] || base[idx] : base[idx]; } - private _ansiStateToStyle(state: IAnsiState): string { + #ansiStateToStyle(state: IAnsiState): string { const styles: string[] = []; if (state.fg) styles.push('color: ' + state.fg); if (state.bg) styles.push('background-color: ' + state.bg); diff --git a/apps/rush/src/MinimalRushConfiguration.ts b/apps/rush/src/MinimalRushConfiguration.ts index 0cc4436b964..1f6923f97eb 100644 --- a/apps/rush/src/MinimalRushConfiguration.ts +++ b/apps/rush/src/MinimalRushConfiguration.ts @@ -18,13 +18,13 @@ interface IMinimalRushConfigurationJson { * decide which version of Rush should be installed/used. */ export class MinimalRushConfiguration { - private _rushVersion: string; - private _commonRushConfigFolder: string; + #rushVersion: string; + #commonRushConfigFolder: string; private constructor(minimalRushConfigurationJson: IMinimalRushConfigurationJson, rushJsonFilename: string) { - this._rushVersion = + this.#rushVersion = minimalRushConfigurationJson.rushVersion || minimalRushConfigurationJson.rushMinimumVersion; - this._commonRushConfigFolder = path.join( + this.#commonRushConfigFolder = path.join( path.dirname(rushJsonFilename), RushConstants.commonFolderName, 'config', @@ -54,7 +54,7 @@ export class MinimalRushConfiguration { * a semver style version number like "4.0.0" */ public get rushVersion(): string { - return this._rushVersion; + return this.#rushVersion; } /** @@ -66,7 +66,7 @@ export class MinimalRushConfiguration { * Example: "C:\MyRepo\common\config\rush" */ public get commonRushConfigFolder(): string { - return this._commonRushConfigFolder; + return this.#commonRushConfigFolder; } } diff --git a/apps/rush/src/RushVersionSelector.ts b/apps/rush/src/RushVersionSelector.ts index 615aaa0e356..10e391fad3d 100644 --- a/apps/rush/src/RushVersionSelector.ts +++ b/apps/rush/src/RushVersionSelector.ts @@ -15,12 +15,12 @@ import type { MinimalRushConfiguration } from './MinimalRushConfiguration'; const MAX_INSTALL_ATTEMPTS: number = 3; export class RushVersionSelector { - private _rushGlobalFolder: _RushGlobalFolder; - private _currentPackageVersion: string; + #rushGlobalFolder: _RushGlobalFolder; + #currentPackageVersion: string; public constructor(currentPackageVersion: string) { - this._rushGlobalFolder = new _RushGlobalFolder(); - this._currentPackageVersion = currentPackageVersion; + this.#rushGlobalFolder = new _RushGlobalFolder(); + this.#currentPackageVersion = currentPackageVersion; } public async ensureRushVersionInstalledAsync( @@ -29,7 +29,7 @@ export class RushVersionSelector { executeOptions: ILaunchOptions ): Promise { const isLegacyRushVersion: boolean = semver.lt(version, '4.0.0'); - const expectedRushPath: string = path.join(this._rushGlobalFolder.nodeSpecificPath, `rush-${version}`); + const expectedRushPath: string = path.join(this.#rushGlobalFolder.nodeSpecificPath, `rush-${version}`); const installMarker: _FlagFile = new _FlagFile(expectedRushPath, 'last-install', { node: process.versions.node @@ -97,7 +97,7 @@ export class RushVersionSelector { }); const rushCliEntrypoint: typeof import('@microsoft/rush-lib') = require(rushLibEntrypoint); // For newer rush-lib, RushCommandSelector can test whether "rushx" is supported or not - RushCommandSelector.execute(this._currentPackageVersion, rushCliEntrypoint, executeOptions); + RushCommandSelector.execute(this.#currentPackageVersion, rushCliEntrypoint, executeOptions); } } } diff --git a/apps/trace-import/src/TraceImportCommandLineParser.ts b/apps/trace-import/src/TraceImportCommandLineParser.ts index f34cec4a90d..a5bd3099135 100644 --- a/apps/trace-import/src/TraceImportCommandLineParser.ts +++ b/apps/trace-import/src/TraceImportCommandLineParser.ts @@ -14,10 +14,10 @@ import { Colorize } from '@rushstack/terminal'; import { type ResolutionType, traceImport } from './traceImport'; export class TraceImportCommandLineParser extends CommandLineParser { - private readonly _debugParameter: CommandLineFlagParameter; - private readonly _pathParameter: IRequiredCommandLineStringParameter; - private readonly _baseFolderParameter: CommandLineStringParameter; - private readonly _resolutionTypeParameter: IRequiredCommandLineChoiceParameter; + readonly #debugParameter: CommandLineFlagParameter; + readonly #pathParameter: IRequiredCommandLineStringParameter; + readonly #baseFolderParameter: CommandLineStringParameter; + readonly #resolutionTypeParameter: IRequiredCommandLineChoiceParameter; public constructor() { super({ @@ -30,13 +30,13 @@ export class TraceImportCommandLineParser extends CommandLineParser { 'print the .d.ts file path that would be resolved by a TypeScript import statement.' }); - this._debugParameter = this.defineFlagParameter({ + this.#debugParameter = this.defineFlagParameter({ parameterLongName: '--debug', parameterShortName: '-d', description: 'Show the full call stack if an error occurs while executing the tool' }); - this._pathParameter = this.defineStringParameter({ + this.#pathParameter = this.defineStringParameter({ parameterLongName: '--path', parameterShortName: '-p', description: @@ -46,7 +46,7 @@ export class TraceImportCommandLineParser extends CommandLineParser { required: true }); - this._baseFolderParameter = this.defineStringParameter({ + this.#baseFolderParameter = this.defineStringParameter({ parameterLongName: '--base-folder', parameterShortName: '-b', description: @@ -55,7 +55,7 @@ export class TraceImportCommandLineParser extends CommandLineParser { argumentName: 'FOLDER_PATH' }); - this._resolutionTypeParameter = this.defineChoiceParameter({ + this.#resolutionTypeParameter = this.defineChoiceParameter({ parameterLongName: '--resolution-type', parameterShortName: '-t', description: @@ -67,17 +67,17 @@ export class TraceImportCommandLineParser extends CommandLineParser { } protected override async onExecuteAsync(): Promise { - if (this._debugParameter.value) { + if (this.#debugParameter.value) { InternalError.breakInDebugger = true; } try { traceImport({ - importPath: this._pathParameter.value, - baseFolder: this._baseFolderParameter.value, - resolutionType: this._resolutionTypeParameter.value + importPath: this.#pathParameter.value, + baseFolder: this.#baseFolderParameter.value, + resolutionType: this.#resolutionTypeParameter.value }); } catch (error) { - if (this._debugParameter.value) { + if (this.#debugParameter.value) { console.error('\n' + error.stack); } else { console.error('\n' + Colorize.red('ERROR: ' + error.message.trim())); diff --git a/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts b/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts index 594ad4c9ab6..0b7081bf149 100644 --- a/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts +++ b/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts @@ -15,15 +15,15 @@ import type { IZipSyncMode, ZipSyncOptionCompression } from '../zipSyncUtils'; import { pack, unpack } from '../index'; export class ZipSyncCommandLineParser extends CommandLineParser { - private readonly _debugParameter: CommandLineFlagParameter; - private readonly _verboseParameter: CommandLineFlagParameter; - private readonly _modeParameter: IRequiredCommandLineChoiceParameter; - private readonly _archivePathParameter: IRequiredCommandLineStringParameter; - private readonly _baseDirParameter: IRequiredCommandLineStringParameter; - private readonly _targetDirectoriesParameter: CommandLineStringListParameter; - private readonly _compressionParameter: IRequiredCommandLineChoiceParameter; - private readonly _terminal: ITerminal; - private readonly _terminalProvider: ConsoleTerminalProvider; + readonly #debugParameter: CommandLineFlagParameter; + readonly #verboseParameter: CommandLineFlagParameter; + readonly #modeParameter: IRequiredCommandLineChoiceParameter; + readonly #archivePathParameter: IRequiredCommandLineStringParameter; + readonly #baseDirParameter: IRequiredCommandLineStringParameter; + readonly #targetDirectoriesParameter: CommandLineStringListParameter; + readonly #compressionParameter: IRequiredCommandLineChoiceParameter; + readonly #terminal: ITerminal; + readonly #terminalProvider: ConsoleTerminalProvider; public constructor(terminalProvider: ConsoleTerminalProvider, terminal: ITerminal) { super({ @@ -31,22 +31,22 @@ export class ZipSyncCommandLineParser extends CommandLineParser { toolDescription: '' }); - this._terminal = terminal; - this._terminalProvider = terminalProvider; + this.#terminal = terminal; + this.#terminalProvider = terminalProvider; - this._debugParameter = this.defineFlagParameter({ + this.#debugParameter = this.defineFlagParameter({ parameterLongName: '--debug', parameterShortName: '-d', description: 'Show the full call stack if an error occurs while executing the tool' }); - this._verboseParameter = this.defineFlagParameter({ + this.#verboseParameter = this.defineFlagParameter({ parameterLongName: '--verbose', parameterShortName: '-v', description: 'Show verbose output' }); - this._modeParameter = this.defineChoiceParameter({ + this.#modeParameter = this.defineChoiceParameter({ parameterLongName: '--mode', parameterShortName: '-m', description: @@ -55,7 +55,7 @@ export class ZipSyncCommandLineParser extends CommandLineParser { required: true }); - this._archivePathParameter = this.defineStringParameter({ + this.#archivePathParameter = this.defineStringParameter({ parameterLongName: '--archive-path', parameterShortName: '-a', description: 'Zip file path', @@ -63,7 +63,7 @@ export class ZipSyncCommandLineParser extends CommandLineParser { required: true }); - this._targetDirectoriesParameter = this.defineStringListParameter({ + this.#targetDirectoriesParameter = this.defineStringListParameter({ parameterLongName: '--target-directory', parameterShortName: '-t', description: 'Target directories to pack or unpack', @@ -71,7 +71,7 @@ export class ZipSyncCommandLineParser extends CommandLineParser { required: true }); - this._baseDirParameter = this.defineStringParameter({ + this.#baseDirParameter = this.defineStringParameter({ parameterLongName: '--base-dir', parameterShortName: '-b', description: 'Base directory for relative paths within the archive', @@ -79,7 +79,7 @@ export class ZipSyncCommandLineParser extends CommandLineParser { required: true }); - this._compressionParameter = this.defineChoiceParameter({ + this.#compressionParameter = this.defineChoiceParameter({ parameterLongName: '--compression', parameterShortName: '-z', description: @@ -90,34 +90,34 @@ export class ZipSyncCommandLineParser extends CommandLineParser { } protected override async onExecuteAsync(): Promise { - if (this._debugParameter.value) { + if (this.#debugParameter.value) { // eslint-disable-next-line no-debugger debugger; - this._terminalProvider.debugEnabled = true; - this._terminalProvider.verboseEnabled = true; + this.#terminalProvider.debugEnabled = true; + this.#terminalProvider.verboseEnabled = true; } - if (this._verboseParameter.value) { - this._terminalProvider.verboseEnabled = true; + if (this.#verboseParameter.value) { + this.#terminalProvider.verboseEnabled = true; } try { - if (this._modeParameter.value === 'pack') { + if (this.#modeParameter.value === 'pack') { pack({ - terminal: this._terminal, - archivePath: this._archivePathParameter.value, - targetDirectories: this._targetDirectoriesParameter.values, - baseDir: this._baseDirParameter.value, - compression: this._compressionParameter.value + terminal: this.#terminal, + archivePath: this.#archivePathParameter.value, + targetDirectories: this.#targetDirectoriesParameter.values, + baseDir: this.#baseDirParameter.value, + compression: this.#compressionParameter.value }); - } else if (this._modeParameter.value === 'unpack') { + } else if (this.#modeParameter.value === 'unpack') { unpack({ - terminal: this._terminal, - archivePath: this._archivePathParameter.value, - targetDirectories: this._targetDirectoriesParameter.values, - baseDir: this._baseDirParameter.value + terminal: this.#terminal, + archivePath: this.#archivePathParameter.value, + targetDirectories: this.#targetDirectoriesParameter.values, + baseDir: this.#baseDirParameter.value }); } } catch (error) { - this._terminal.writeErrorLine('\n' + error.stack); + this.#terminal.writeErrorLine('\n' + error.stack); } } } diff --git a/libraries/rush-daemon-transport/src/DaemonFrameConnection.ts b/libraries/rush-daemon-transport/src/DaemonFrameConnection.ts index dce6c5a545d..00622465f5b 100644 --- a/libraries/rush-daemon-transport/src/DaemonFrameConnection.ts +++ b/libraries/rush-daemon-transport/src/DaemonFrameConnection.ts @@ -16,48 +16,48 @@ import { DaemonTransportError, DaemonTransportErrorCode } from './DaemonTranspor * slow consumer loses nothing. * @beta */ export class DaemonFrameConnection { - private readonly _socket: net.Socket; - private readonly _decoder: DaemonFrameDecoder = new DaemonFrameDecoder(); - private _frameHandler: ((frame: IDaemonFrame) => void) | undefined; - private _closedHandler: ((error: Error | undefined) => void) | undefined; - private _closedError: Error | undefined; + readonly #socket: net.Socket; + readonly #decoder: DaemonFrameDecoder = new DaemonFrameDecoder(); + #frameHandler: ((frame: IDaemonFrame) => void) | undefined; + #closedHandler: ((error: Error | undefined) => void) | undefined; + #closedError: Error | undefined; public constructor(socket: net.Socket) { - this._socket = socket; - socket.on('data', (chunk: Buffer) => this._onData(chunk)); - socket.on('error', (error: Error) => this._onError(error)); - socket.on('close', () => this._onClose()); + this.#socket = socket; + socket.on('data', (chunk: Buffer) => this.#onData(chunk)); + socket.on('error', (error: Error) => this.#onError(error)); + socket.on('close', () => this.#onClose()); } /** Registers the frame handler invoked for each decoded frame. */ public onFrame(handler: (frame: IDaemonFrame) => void): void { - this._frameHandler = handler; + this.#frameHandler = handler; } /** Registers the close handler, invoked at most once with the cause. */ public onClosed(handler: (error: Error | undefined) => void): void { - this._closedHandler = handler; + this.#closedHandler = handler; } /** Encodes and writes a frame, resolving when the socket has drained it. @throws {@link DaemonTransportError} when closed. */ public async sendFrameAsync(frame: IDaemonFrame): Promise { - this._assertOpen(); - if (!this._socket.write(encodeDaemonFrame(frame))) { - await once(this._socket, 'drain'); + this.#assertOpen(); + if (!this.#socket.write(encodeDaemonFrame(frame))) { + await once(this.#socket, 'drain'); } } /** Half-closes the writable side and releases the socket. */ public async closeAsync(): Promise { - this._socket.end(); - this._socket.destroySoon(); + this.#socket.end(); + this.#socket.destroySoon(); } /** The wrapped socket, for the internal raw-write test hook. @internal */ public get socket(): net.Socket { - return this._socket; + return this.#socket; } - private _assertOpen(): void { - if (this._closedError !== undefined || this._socket.closed) { + #assertOpen(): void { + if (this.#closedError !== undefined || this.#socket.closed) { throw new DaemonTransportError( DaemonTransportErrorCode.transportClosed, 'Cannot send a frame on a closed connection.' @@ -65,36 +65,36 @@ export class DaemonFrameConnection { } } - private _onData(chunk: Buffer): void { + #onData(chunk: Buffer): void { let frames: IDaemonFrame[]; try { - frames = this._decoder.push(chunk); + frames = this.#decoder.push(chunk); } catch (error) { - this._fail(error); + this.#fail(error); return; } for (const frame of frames) { - this._dispatchFrame(frame); + this.#dispatchFrame(frame); } } - private _dispatchFrame(frame: IDaemonFrame): void { + #dispatchFrame(frame: IDaemonFrame): void { try { - this._frameHandler?.(frame); + this.#frameHandler?.(frame); } catch (error) { - this._fail(error); + this.#fail(error); } } - private _fail(error: unknown): void { + #fail(error: unknown): void { const cause: Error = error instanceof Error ? error : new Error(String(error)); - this._closedError = this._closedError ?? cause; - this._socket.destroy(cause); + this.#closedError = this.#closedError ?? cause; + this.#socket.destroy(cause); } - private _onError(error: Error): void { - this._closedError = this._closedError ?? error; + #onError(error: Error): void { + this.#closedError = this.#closedError ?? error; } - private _onClose(): void { - this._closedHandler?.(this._closedError); + #onClose(): void { + this.#closedHandler?.(this.#closedError); } } diff --git a/libraries/rush-daemon-transport/src/DaemonListener.ts b/libraries/rush-daemon-transport/src/DaemonListener.ts index f4b17aefb43..30e55b7d630 100644 --- a/libraries/rush-daemon-transport/src/DaemonListener.ts +++ b/libraries/rush-daemon-transport/src/DaemonListener.ts @@ -33,11 +33,11 @@ export interface IDaemonListenerOptions { * `daemonAlreadyRunning` transport error is thrown. * @beta */ export class DaemonFrameListener { - private readonly _server: net.Server; - private readonly _paths: IDaemonPaths; + readonly #server: net.Server; + readonly #paths: IDaemonPaths; private constructor(server: net.Server, paths: IDaemonPaths) { - this._server = server; - this._paths = paths; + this.#server = server; + this.#paths = paths; } /** Binds the socket/pipe path and writes the PID lockfile. */ public static async listenAsync( @@ -62,8 +62,8 @@ export class DaemonFrameListener { /** Stops accepting connections and releases the socket/pipe and lockfile. */ public async closeAsync(): Promise { - await new Promise((resolve: () => void) => this._server.close(() => resolve())); - removeDaemonArtifacts(this._paths.lockfilePath, this._paths.socketPath); + await new Promise((resolve: () => void) => this.#server.close(() => resolve())); + removeDaemonArtifacts(this.#paths.lockfilePath, this.#paths.socketPath); } } diff --git a/libraries/rush-daemon/src/DaemonControlSession.ts b/libraries/rush-daemon/src/DaemonControlSession.ts index 85c38211322..f9bb521919c 100644 --- a/libraries/rush-daemon/src/DaemonControlSession.ts +++ b/libraries/rush-daemon/src/DaemonControlSession.ts @@ -27,23 +27,23 @@ export interface IDaemonControlSessionOptions { } export class DaemonControlSession { - private readonly _connection: DaemonFrameConnection; - private readonly _options: IDaemonControlSessionOptions; - private _handshakeComplete: boolean = false; - private _sendQueue: Promise = Promise.resolve(); + readonly #connection: DaemonFrameConnection; + readonly #options: IDaemonControlSessionOptions; + #handshakeComplete: boolean = false; + #sendQueue: Promise = Promise.resolve(); public constructor(connection: DaemonFrameConnection, options: IDaemonControlSessionOptions) { - this._connection = connection; - this._options = options; - connection.onFrame((frame: IDaemonFrame) => this._onFrame(frame)); + this.#connection = connection; + this.#options = options; + connection.onFrame((frame: IDaemonFrame) => this.#onFrame(frame)); connection.onClosed((error: Error | undefined) => options.onClosed(this, error)); } public closeAsync(): Promise { - return this._connection.closeAsync(); + return this.#connection.closeAsync(); } - private _onFrame(frame: IDaemonFrame): void { + #onFrame(frame: IDaemonFrame): void { if (frame.kind !== DaemonFrameType.controlJson) { throw new DaemonProtocolError( 'malformedControlMessage', @@ -51,10 +51,10 @@ export class DaemonControlSession { ); } const message: DaemonControlMessage = decodeDaemonControlMessage(frame.payload); - if (!this._handshakeComplete) { - this._handleHello(message); + if (!this.#handshakeComplete) { + this.#handleHello(message); } else if (message.kind === 'ping') { - this._send(this._createPong()); + this.#send(this.#createPong()); } else { throw new DaemonProtocolError( 'malformedControlMessage', @@ -63,7 +63,7 @@ export class DaemonControlSession { } } - private _handleHello(message: DaemonControlMessage): void { + #handleHello(message: DaemonControlMessage): void { if (message.kind !== 'hello') { throw new DaemonProtocolError( 'malformedControlMessage', @@ -76,42 +76,42 @@ export class DaemonControlSession { randomUUID() ); if (outcome.accepted) { - this._handshakeComplete = true; - this._send(outcome.ack); + this.#handshakeComplete = true; + this.#send(outcome.ack); } else { const errorMessage: IDaemonErrorMessage = { kind: 'error', payload: { code: outcome.error.code, message: outcome.error.message } }; - this._send(errorMessage, true); + this.#send(errorMessage, true); } } - private _createPong(): IDaemonPongMessage { + #createPong(): IDaemonPongMessage { return { kind: 'pong', payload: { - daemonVersion: this._options.daemonVersion, + daemonVersion: this.#options.daemonVersion, protocolVersion: DAEMON_PROTOCOL_VERSION, - uptimeMs: Date.now() - this._options.startedAtMs + uptimeMs: Date.now() - this.#options.startedAtMs } }; } - private _send(message: DaemonControlMessage, closeAfterSend: boolean = false): void { + #send(message: DaemonControlMessage, closeAfterSend: boolean = false): void { const frame: IDaemonFrame = { kind: DaemonFrameType.controlJson, payload: encodeDaemonControlMessage(message) }; - this._sendQueue = this._sendQueue - .then(() => this._connection.sendFrameAsync(frame)) - .then(() => (closeAfterSend ? this._connection.closeAsync() : undefined)) - .catch((error: unknown) => this._handleSendErrorAsync(error)); + this.#sendQueue = this.#sendQueue + .then(() => this.#connection.sendFrameAsync(frame)) + .then(() => (closeAfterSend ? this.#connection.closeAsync() : undefined)) + .catch((error: unknown) => this.#handleSendErrorAsync(error)); } - private async _handleSendErrorAsync(error: unknown): Promise { + async #handleSendErrorAsync(error: unknown): Promise { const normalizedError: Error = error instanceof Error ? error : new Error(String(error)); - this._options.onError(normalizedError); - await this._connection.closeAsync(); + this.#options.onError(normalizedError); + await this.#connection.closeAsync(); } } diff --git a/libraries/rush-daemon/src/RequestScheduler.ts b/libraries/rush-daemon/src/RequestScheduler.ts index 7548ac7819c..dc5804f28ca 100644 --- a/libraries/rush-daemon/src/RequestScheduler.ts +++ b/libraries/rush-daemon/src/RequestScheduler.ts @@ -101,22 +101,22 @@ interface IQueuedRequest { * @public */ export class RequestScheduler { - private readonly _queue: IQueuedRequest[] = []; - private _activeClass: RequestExclusivityClass | undefined; - private _activeRequestCount: number = 0; + readonly #queue: IQueuedRequest[] = []; + #activeClass: RequestExclusivityClass | undefined; + #activeRequestCount: number = 0; /** * The number of requests currently waiting for admission. */ public get queuedRequestCount(): number { - return this._queue.length; + return this.#queue.length; } /** * The number of requests that currently hold a lease. */ public get activeRequestCount(): number { - return this._activeRequestCount; + return this.#activeRequestCount; } /** @@ -124,7 +124,7 @@ export class RequestScheduler { */ public acquireAsync(options: IRequestSchedulerAcquireOptions): Promise { try { - this._validateOptions(options); + this.#validateOptions(options); } catch (error) { return Promise.reject(error); } @@ -135,8 +135,8 @@ export class RequestScheduler { ); } - if (this._queue.length === 0 && this._canAdmit(options.exclusivityClass)) { - return Promise.resolve(this._createLease(options.exclusivityClass)); + if (this.#queue.length === 0 && this.#canAdmit(options.exclusivityClass)) { + return Promise.resolve(this.#createLease(options.exclusivityClass)); } if (options.noWait) { @@ -159,7 +159,7 @@ export class RequestScheduler { if (options.waitTimeoutMs !== undefined) { request.timeout = setTimeout(() => { - this._rejectQueuedRequest( + this.#rejectQueuedRequest( request, new RequestSchedulerError( RequestSchedulerErrorCode.WaitTimeout, @@ -171,20 +171,20 @@ export class RequestScheduler { if (options.abortSignal) { request.abortListener = () => { - this._rejectQueuedRequest( + this.#rejectQueuedRequest( request, new RequestSchedulerError(RequestSchedulerErrorCode.Aborted, 'The request was aborted while waiting.') ); }; options.abortSignal.addEventListener('abort', request.abortListener, { once: true }); } - this._queue.push(request); - this._notifyQueuePositions(); - this._drainQueue(); + this.#queue.push(request); + this.#notifyQueuePositions(); + this.#drainQueue(); }); } - private _validateOptions(options: IRequestSchedulerAcquireOptions): void { + #validateOptions(options: IRequestSchedulerAcquireOptions): void { if ( options.waitTimeoutMs !== undefined && (!Number.isFinite(options.waitTimeoutMs) || @@ -195,19 +195,19 @@ export class RequestScheduler { } } - private _canAdmit(exclusivityClass: RequestExclusivityClass): boolean { - if (this._activeRequestCount === 0) { + #canAdmit(exclusivityClass: RequestExclusivityClass): boolean { + if (this.#activeRequestCount === 0) { return true; } return ( - exclusivityClass !== RequestExclusivityClass.Exclusive && exclusivityClass === this._activeClass + exclusivityClass !== RequestExclusivityClass.Exclusive && exclusivityClass === this.#activeClass ); } - private _createLease(exclusivityClass: RequestExclusivityClass): IRequestLease { - this._activeClass = exclusivityClass; - this._activeRequestCount++; + #createLease(exclusivityClass: RequestExclusivityClass): IRequestLease { + this.#activeClass = exclusivityClass; + this.#activeRequestCount++; let released: boolean = false; return { @@ -218,48 +218,48 @@ export class RequestScheduler { } released = true; - this._activeRequestCount--; - if (this._activeRequestCount === 0) { - this._activeClass = undefined; + this.#activeRequestCount--; + if (this.#activeRequestCount === 0) { + this.#activeClass = undefined; } - this._drainQueue(); + this.#drainQueue(); } }; } - private _drainQueue(): void { + #drainQueue(): void { let admittedRequest: boolean = false; - while (this._queue.length > 0) { - const request: IQueuedRequest = this._queue[0]; - if (!this._canAdmit(request.options.exclusivityClass)) { + while (this.#queue.length > 0) { + const request: IQueuedRequest = this.#queue[0]; + if (!this.#canAdmit(request.options.exclusivityClass)) { break; } - this._queue.shift(); - this._cleanupQueuedRequest(request); - request.resolve(this._createLease(request.options.exclusivityClass)); + this.#queue.shift(); + this.#cleanupQueuedRequest(request); + request.resolve(this.#createLease(request.options.exclusivityClass)); admittedRequest = true; } if (admittedRequest) { - this._notifyQueuePositions(); + this.#notifyQueuePositions(); } } - private _rejectQueuedRequest(request: IQueuedRequest, error: Error): void { - const index: number = this._queue.indexOf(request); + #rejectQueuedRequest(request: IQueuedRequest, error: Error): void { + const index: number = this.#queue.indexOf(request); if (index < 0) { return; } - this._queue.splice(index, 1); - this._cleanupQueuedRequest(request); + this.#queue.splice(index, 1); + this.#cleanupQueuedRequest(request); request.reject(error); - this._notifyQueuePositions(); - this._drainQueue(); + this.#notifyQueuePositions(); + this.#drainQueue(); } - private _cleanupQueuedRequest(request: IQueuedRequest): void { + #cleanupQueuedRequest(request: IQueuedRequest): void { if (request.timeout) { clearTimeout(request.timeout); request.timeout = undefined; @@ -270,10 +270,10 @@ export class RequestScheduler { } } - private _notifyQueuePositions(): void { - for (let index: number = 0; index < this._queue.length; index++) { + #notifyQueuePositions(): void { + for (let index: number = 0; index < this.#queue.length; index++) { try { - this._queue[index].options.onQueuePositionChanged?.(index + 1); + this.#queue[index].options.onQueuePositionChanged?.(index + 1); } catch (error) { process.emitWarning(error instanceof Error ? error : String(error), { code: 'RUSH_DAEMON_QUEUE_POSITION_CALLBACK_ERROR' diff --git a/libraries/rush-daemon/src/RushDaemonHost.ts b/libraries/rush-daemon/src/RushDaemonHost.ts index f3a9cd42510..f63816d1759 100644 --- a/libraries/rush-daemon/src/RushDaemonHost.ts +++ b/libraries/rush-daemon/src/RushDaemonHost.ts @@ -40,11 +40,11 @@ export interface IRushDaemonHostOptions { * @beta */ export class RushDaemonHost { - private readonly _listener: DaemonFrameListener; - private readonly _sessions: Set; - private readonly _lifecycle: { closing: boolean }; + readonly #listener: DaemonFrameListener; + readonly #sessions: Set; + readonly #lifecycle: { closing: boolean }; public readonly paths: IDaemonPaths; - private _closePromise: Promise | undefined; + #closePromise: Promise | undefined; private constructor( listener: DaemonFrameListener, @@ -52,10 +52,10 @@ export class RushDaemonHost { sessions: Set, lifecycle: { closing: boolean } ) { - this._listener = listener; + this.#listener = listener; this.paths = paths; - this._sessions = sessions; - this._lifecycle = lifecycle; + this.#sessions = sessions; + this.#lifecycle = lifecycle; } /** Resolves only after the transport is bound and its lockfile has been written. */ @@ -96,13 +96,13 @@ export class RushDaemonHost { /** Closes active connections, stops listening, and removes transport artifacts. */ public closeAsync(): Promise { - this._closePromise ??= this._closeOnceAsync(); - return this._closePromise; + this.#closePromise ??= this.#closeOnceAsync(); + return this.#closePromise; } - private async _closeOnceAsync(): Promise { - this._lifecycle.closing = true; - await Promise.all(Array.from(this._sessions, (session: DaemonControlSession) => session.closeAsync())); - await this._listener.closeAsync(); + async #closeOnceAsync(): Promise { + this.#lifecycle.closing = true; + await Promise.all(Array.from(this.#sessions, (session: DaemonControlSession) => session.closeAsync())); + await this.#listener.closeAsync(); } } diff --git a/libraries/rush-terminal-renderer/src/DaemonRendererHost.ts b/libraries/rush-terminal-renderer/src/DaemonRendererHost.ts index cf024c7c033..aba20f2e7e5 100644 --- a/libraries/rush-terminal-renderer/src/DaemonRendererHost.ts +++ b/libraries/rush-terminal-renderer/src/DaemonRendererHost.ts @@ -31,22 +31,22 @@ const CHUNK_DECODER: InstanceType = new TextDecoder('utf8', * @beta */ export class DaemonRendererHost { - private readonly _renderer: IDaemonRenderer; - private readonly _verbosity: DaemonVerbosity; - private readonly _streams: OperationStreamRegistry; - private readonly _router: HostEventRouter; - private readonly _terminal: IDaemonRendererTerminal; + readonly #renderer: IDaemonRenderer; + readonly #verbosity: DaemonVerbosity; + readonly #streams: OperationStreamRegistry; + readonly #router: HostEventRouter; + readonly #terminal: IDaemonRendererTerminal; public constructor(options: IDaemonRendererHostOptions) { - this._terminal = options.terminal; - this._verbosity = options.verbosity ?? DEFAULT_VERBOSITY; - this._renderer = options.renderer ?? new LegacyCollatedRenderer(); - this._streams = new OperationStreamRegistry({ + this.#terminal = options.terminal; + this.#verbosity = options.verbosity ?? DEFAULT_VERBOSITY; + this.#renderer = options.renderer ?? new LegacyCollatedRenderer(); + this.#streams = new OperationStreamRegistry({ destination: new TerminalSinkWritable(options.terminal), removeColors: shouldRemoveColors(options.colorLevel), - quiet: this._verbosity === QUIET_VERBOSITY + quiet: this.#verbosity === QUIET_VERBOSITY }); - this._router = new HostEventRouter(this._streams, this._renderer, this._verbosity); + this.#router = new HostEventRouter(this.#streams, this.#renderer, this.#verbosity); } /** @@ -54,22 +54,22 @@ export class DaemonRendererHost { * {@link DaemonRendererHost.handleEvent} call. */ public async initializeAsync(): Promise { - await this._renderer.initializeAsync({ terminal: this._terminal }); + await this.#renderer.initializeAsync({ terminal: this.#terminal }); } /** Feeds one decoded `0x05` event envelope into the host. */ public handleEvent(envelope: IDaemonEventEnvelope): void { - this._router.routeEvent(envelope); + this.#router.routeEvent(envelope); } /** Feeds one decoded `0x02`/`0x03` log chunk into the collator. */ public handleLogChunk(operationId: string, stream: 'stdout' | 'stderr', chunk: Uint8Array): void { - if (stream === 'stdout' && this._verbosity === QUIET_VERBOSITY) { + if (stream === 'stdout' && this.#verbosity === QUIET_VERBOSITY) { // Match the legacy quiet-mode DiscardStdoutTransform: per-client display // filtering, without mutating the shared stream. return; } - this._streams.writeChunk(operationId, { + this.#streams.writeChunk(operationId, { kind: toChunkKind(stream), text: CHUNK_DECODER.decode(chunk) }); @@ -77,7 +77,7 @@ export class DaemonRendererHost { /** Flushes and closes the renderer. */ public async closeAsync(): Promise { - await this._renderer.flushAsync(); - await this._renderer.closeAsync(); + await this.#renderer.flushAsync(); + await this.#renderer.closeAsync(); } } diff --git a/libraries/rush-terminal-renderer/src/HostEventRouter.ts b/libraries/rush-terminal-renderer/src/HostEventRouter.ts index a5a81ba1676..647750f023c 100644 --- a/libraries/rush-terminal-renderer/src/HostEventRouter.ts +++ b/libraries/rush-terminal-renderer/src/HostEventRouter.ts @@ -26,72 +26,72 @@ function readScopeOperationId(envelope: IDaemonEventEnvelope): string | undefine * @internal */ export class HostEventRouter { - private readonly _streams: OperationStreamRegistry; - private readonly _renderer: IDaemonRenderer; - private readonly _verbosity: DaemonVerbosity; + readonly #streams: OperationStreamRegistry; + readonly #renderer: IDaemonRenderer; + readonly #verbosity: DaemonVerbosity; public constructor( streams: OperationStreamRegistry, renderer: IDaemonRenderer, verbosity: DaemonVerbosity ) { - this._streams = streams; - this._renderer = renderer; - this._verbosity = verbosity; + this.#streams = streams; + this.#renderer = renderer; + this.#verbosity = verbosity; } /** Routes one decoded `0x05` event envelope. */ public routeEvent(envelope: IDaemonEventEnvelope): void { - this._trackOperationLifecycle(envelope); - if (this._routeScopedActivity(envelope)) { + this.#trackOperationLifecycle(envelope); + if (this.#routeScopedActivity(envelope)) { return; } - if (shouldSerializeDaemonEvent(this._verbosity, envelope)) { - this._renderer.report(envelope); + if (shouldSerializeDaemonEvent(this.#verbosity, envelope)) { + this.#renderer.report(envelope); } } - private _trackOperationLifecycle(envelope: IDaemonEventEnvelope): void { + #trackOperationLifecycle(envelope: IDaemonEventEnvelope): void { if (envelope.type === 'operationRegistered') { - this._trackRegistered(envelope.payload as IDaemonOperationRegisteredPayload); + this.#trackRegistered(envelope.payload as IDaemonOperationRegisteredPayload); } if (envelope.type === 'extension') { - this._trackExtension(envelope.payload as IDaemonExtensionEventPayload); + this.#trackExtension(envelope.payload as IDaemonExtensionEventPayload); } } - private _trackRegistered(payload: IDaemonOperationRegisteredPayload): void { + #trackRegistered(payload: IDaemonOperationRegisteredPayload): void { if (!payload.silent) { - this._streams.registerOperation(); + this.#streams.registerOperation(); } } - private _trackExtension(payload: IDaemonExtensionEventPayload): void { + #trackExtension(payload: IDaemonExtensionEventPayload): void { if (payload.name === RUSHD_OPERATION_STREAM_CLOSED) { const data: IDaemonOperationStreamClosedPayload = payload.data as IDaemonOperationStreamClosedPayload; - this._streams.closeOperation(data.operationId); + this.#streams.closeOperation(data.operationId); } } // Operation-scoped activity lines are part of the operation's output block // (legacy writes them to the operation's collated stream, bypassing the // quiet-mode stdout discard), so they route to the collator, not the renderer. - private _routeScopedActivity(envelope: IDaemonEventEnvelope): boolean { + #routeScopedActivity(envelope: IDaemonEventEnvelope): boolean { const operationId: string | undefined = readScopeOperationId(envelope); if (envelope.type !== 'activityChanged' || operationId === undefined) { return false; } - this._writeActivityLine(operationId, envelope.payload); + this.#writeActivityLine(operationId, envelope.payload); return true; } - private _writeActivityLine(operationId: string, payload: unknown): void { + #writeActivityLine(operationId: string, payload: unknown): void { const activity: unknown = payload; const text: unknown = (activity as { text?: unknown }).text; const stream: unknown = (activity as { stream?: unknown }).stream; if (typeof text === 'string') { - this._streams.writeChunk(operationId, { + this.#streams.writeChunk(operationId, { kind: stream === 'stderr' ? TerminalChunkKind.Stderr : TerminalChunkKind.Stdout, text: `${text}\n` }); diff --git a/libraries/rush-terminal-renderer/src/LegacyCollatedRenderer.ts b/libraries/rush-terminal-renderer/src/LegacyCollatedRenderer.ts index 85b86607524..3f1abaac366 100644 --- a/libraries/rush-terminal-renderer/src/LegacyCollatedRenderer.ts +++ b/libraries/rush-terminal-renderer/src/LegacyCollatedRenderer.ts @@ -32,11 +32,11 @@ function isActivityPayload(payload: unknown): payload is IDaemonActivityPayload */ export class LegacyCollatedRenderer implements IDaemonRenderer { public readonly name: string = RENDERER_NAME; - private _terminal: IDaemonRendererTerminal | undefined; + #terminal: IDaemonRendererTerminal | undefined; /** {@inheritDoc IDaemonRenderer.initializeAsync} */ public async initializeAsync(context: IDaemonRendererContext): Promise { - this._terminal = context.terminal; + this.#terminal = context.terminal; } /** {@inheritDoc IDaemonRenderer.report} */ @@ -44,14 +44,14 @@ export class LegacyCollatedRenderer implements IDaemonRenderer { if (event.type !== 'activityChanged' || !isActivityPayload(event.payload)) { return; } - this._writeLine(event.payload.text); + this.#writeLine(event.payload.text); } - private _writeLine(text: string): void { + #writeLine(text: string): void { // Emit the client's OS newline, matching the newline normalization the // collated pipeline applies (TextRewriterTransform OsDefault) so global // status lines and collated blocks are consistent on every platform. - this._terminal?.write(`${text}${EOL}`, 'stdout'); + this.#terminal?.write(`${text}${EOL}`, 'stdout'); } /** {@inheritDoc IDaemonRenderer.flushAsync} */ @@ -61,6 +61,6 @@ export class LegacyCollatedRenderer implements IDaemonRenderer { /** {@inheritDoc IDaemonRenderer.closeAsync} */ public async closeAsync(): Promise { - this._terminal = undefined; + this.#terminal = undefined; } } diff --git a/libraries/rush-terminal-renderer/src/OperationStreamRegistry.ts b/libraries/rush-terminal-renderer/src/OperationStreamRegistry.ts index d761d07a556..f60f3887206 100644 --- a/libraries/rush-terminal-renderer/src/OperationStreamRegistry.ts +++ b/libraries/rush-terminal-renderer/src/OperationStreamRegistry.ts @@ -27,66 +27,66 @@ export interface IOperationStreamRegistryOptions { * @beta */ export class OperationStreamRegistry { - private readonly _collator: StreamCollator; - private readonly _collatedTerminal: CollatedTerminal; - private readonly _writers: Map; - private readonly _quiet: boolean; - private _completedOperations: number; - private _totalOperations: number; + readonly #collator: StreamCollator; + readonly #collatedTerminal: CollatedTerminal; + readonly #writers: Map; + readonly #quiet: boolean; + #completedOperations: number; + #totalOperations: number; public constructor(options: IOperationStreamRegistryOptions) { - this._writers = new Map(); - this._quiet = options.quiet; - this._completedOperations = 0; - this._totalOperations = 0; + this.#writers = new Map(); + this.#quiet = options.quiet; + this.#completedOperations = 0; + this.#totalOperations = 0; const transform: TextRewriterTransform = new TextRewriterTransform({ destination: options.destination, normalizeNewlines: NewlineKind.OsDefault, removeColors: options.removeColors }); - this._collatedTerminal = new CollatedTerminal(transform); - this._collator = new StreamCollator({ + this.#collatedTerminal = new CollatedTerminal(transform); + this.#collator = new StreamCollator({ destination: transform, - onWriterActive: (writer: CollatedWriter | undefined) => this._onWriterActive(writer) + onWriterActive: (writer: CollatedWriter | undefined) => this.#onWriterActive(writer) }); } /** Increments the total-operation count shown in headers. */ public registerOperation(): void { - this._totalOperations += 1; + this.#totalOperations += 1; } /** Writes one raw chunk to the operation's collated stream. */ public writeChunk(operationId: string, chunk: ITerminalChunk): void { - let writer: CollatedWriter | undefined = this._writers.get(operationId); + let writer: CollatedWriter | undefined = this.#writers.get(operationId); if (writer === undefined) { - writer = this._collator.registerTask(operationId); - this._writers.set(operationId, writer); + writer = this.#collator.registerTask(operationId); + this.#writers.set(operationId, writer); } writer.writeChunk(chunk); } /** Closes the operation's stream, flushing its collated output. */ public closeOperation(operationId: string): void { - const writer: CollatedWriter | undefined = this._writers.get(operationId); + const writer: CollatedWriter | undefined = this.#writers.get(operationId); if (writer !== undefined && writer.isOpen) { writer.close(); } } - private _onWriterActive(writer: CollatedWriter | undefined): void { + #onWriterActive(writer: CollatedWriter | undefined): void { if (writer === undefined) { return; } - this._completedOperations += 1; + this.#completedOperations += 1; const header: string = formatDaemonOperationHeader( writer.taskName, - this._completedOperations, - this._totalOperations + this.#completedOperations, + this.#totalOperations ); - this._collatedTerminal.writeStdoutLine(`\n${header}`); - if (!this._quiet) { - this._collatedTerminal.writeStdoutLine(''); + this.#collatedTerminal.writeStdoutLine(`\n${header}`); + if (!this.#quiet) { + this.#collatedTerminal.writeStdoutLine(''); } } } diff --git a/libraries/rush-terminal-renderer/src/TerminalSinkWritable.ts b/libraries/rush-terminal-renderer/src/TerminalSinkWritable.ts index 6ff84cdd5d4..73ecb3f0324 100644 --- a/libraries/rush-terminal-renderer/src/TerminalSinkWritable.ts +++ b/libraries/rush-terminal-renderer/src/TerminalSinkWritable.ts @@ -12,15 +12,15 @@ import type { IDaemonRendererTerminal } from './DaemonRendererTerminal'; * @beta */ export class TerminalSinkWritable extends TerminalWritable { - private readonly _terminal: IDaemonRendererTerminal; + readonly #terminal: IDaemonRendererTerminal; public constructor(terminal: IDaemonRendererTerminal) { super({ preventAutoclose: true }); - this._terminal = terminal; + this.#terminal = terminal; } /** {@inheritDoc @rushstack/terminal#TerminalWritable.onWriteChunk} */ public onWriteChunk(chunk: ITerminalChunk): void { - this._terminal.write(chunk.text, chunk.kind === TerminalChunkKind.Stderr ? 'stderr' : 'stdout'); + this.#terminal.write(chunk.text, chunk.kind === TerminalChunkKind.Stderr ? 'stderr' : 'stdout'); } } diff --git a/libraries/rush-terminal-renderer/src/test/LegacyPipelineReplica.ts b/libraries/rush-terminal-renderer/src/test/LegacyPipelineReplica.ts index 38318c06c56..7877d187e2a 100644 --- a/libraries/rush-terminal-renderer/src/test/LegacyPipelineReplica.ts +++ b/libraries/rush-terminal-renderer/src/test/LegacyPipelineReplica.ts @@ -28,63 +28,63 @@ const LEGACY_MIN_MIDDLE: number = 0; /** Replicates the legacy in-process collated output pipeline. */ export class LegacyPipelineReplica { - private readonly _collator: StreamCollator; - private readonly _terminal: CollatedTerminal; - private readonly _writers: Map; - private readonly _quiet: boolean; - private readonly _total: number; - private _completed: number; + readonly #collator: StreamCollator; + readonly #terminal: CollatedTerminal; + readonly #writers: Map; + readonly #quiet: boolean; + readonly #total: number; + #completed: number; public constructor(destination: TerminalWritable, totalOperations: number, quiet: boolean) { - this._writers = new Map(); - this._quiet = quiet; - this._total = totalOperations; - this._completed = INITIAL_COUNT; + this.#writers = new Map(); + this.#quiet = quiet; + this.#total = totalOperations; + this.#completed = INITIAL_COUNT; const transform: TextRewriterTransform = new TextRewriterTransform({ destination, normalizeNewlines: NewlineKind.OsDefault, removeColors: true }); - this._terminal = new CollatedTerminal(transform); - this._collator = new StreamCollator({ + this.#terminal = new CollatedTerminal(transform); + this.#collator = new StreamCollator({ destination: transform, - onWriterActive: (writer: CollatedWriter | undefined) => this._legacyOnWriterActive(writer) + onWriterActive: (writer: CollatedWriter | undefined) => this.#legacyOnWriterActive(writer) }); } public writeChunk(operationId: string, chunk: ITerminalChunk): void { // Legacy quiet mode installs a DiscardStdoutTransform upstream of the collator. - if (this._isDiscarded(chunk)) { + if (this.#isDiscarded(chunk)) { return; } - let writer: CollatedWriter | undefined = this._writers.get(operationId); + let writer: CollatedWriter | undefined = this.#writers.get(operationId); if (writer === undefined) { - writer = this._collator.registerTask(operationId); - this._writers.set(operationId, writer); + writer = this.#collator.registerTask(operationId); + this.#writers.set(operationId, writer); } writer.writeChunk(chunk); } - private _isDiscarded(chunk: ITerminalChunk): boolean { - return this._quiet && chunk.kind === TerminalChunkKind.Stdout; + #isDiscarded(chunk: ITerminalChunk): boolean { + return this.#quiet && chunk.kind === TerminalChunkKind.Stdout; } public closeOperation(operationId: string): void { - const writer: CollatedWriter | undefined = this._writers.get(operationId); + const writer: CollatedWriter | undefined = this.#writers.get(operationId); if (writer !== undefined && writer.isOpen) { writer.close(); } } - private _legacyOnWriterActive(writer: CollatedWriter | undefined): void { + #legacyOnWriterActive(writer: CollatedWriter | undefined): void { if (!writer) { return; } - this._completed += 1; + this.#completed += 1; const leftPart: string = Colorize.gray('==[') + ' ' + Colorize.cyan(writer.taskName) + ' '; const leftPartLength: number = LEGACY_LEFT_BRACKET_CHARS + writer.taskName.length + LEGACY_NAME_PADDING; - const completedOfTotal: string = `${this._completed} of ${this._total}`; + const completedOfTotal: string = `${this.#completed} of ${this.#total}`; const rightPart: string = ' ' + Colorize.white(completedOfTotal) + ' ' + Colorize.gray(']=='); const rightPartLength: number = LEGACY_COUNT_PADDING + completedOfTotal.length + LEGACY_RIGHT_BRACKET_CHARS; const middleLength: number = Math.max( @@ -92,9 +92,9 @@ export class LegacyPipelineReplica { LEGACY_MIN_MIDDLE ); const middlePart: string = Colorize.gray(']' + '='.repeat(middleLength) + '['); - this._terminal.writeStdoutLine('\n' + leftPart + middlePart + rightPart); - if (!this._quiet) { - this._terminal.writeStdoutLine(''); + this.#terminal.writeStdoutLine('\n' + leftPart + middlePart + rightPart); + if (!this.#quiet) { + this.#terminal.writeStdoutLine(''); } } } diff --git a/libraries/rush-terminal-renderer/src/test/TestTerminal.ts b/libraries/rush-terminal-renderer/src/test/TestTerminal.ts index ebf6cadf4b8..315fec686c3 100644 --- a/libraries/rush-terminal-renderer/src/test/TestTerminal.ts +++ b/libraries/rush-terminal-renderer/src/test/TestTerminal.ts @@ -21,15 +21,15 @@ export class CollectingWritable extends TerminalWritable { /** The concatenated text of all stdout chunks. */ public get stdout(): string { - return this._collect(TerminalChunkKind.Stdout); + return this.#collect(TerminalChunkKind.Stdout); } /** The concatenated text of all stderr chunks. */ public get stderr(): string { - return this._collect(TerminalChunkKind.Stderr); + return this.#collect(TerminalChunkKind.Stderr); } - private _collect(kind: TerminalChunkKind): string { + #collect(kind: TerminalChunkKind): string { return this.chunks .filter((chunk: ITerminalChunk) => chunk.kind === kind) .map((chunk: ITerminalChunk) => chunk.text) @@ -41,24 +41,24 @@ export class CollectingWritable extends TerminalWritable { export class TestTerminal implements IDaemonRendererTerminal { public readonly columns: number = DEFAULT_TEST_COLUMNS; public readonly isTTY: boolean = false; - private readonly _writes: [DaemonRenderStream, string][] = []; + readonly #writes: [DaemonRenderStream, string][] = []; public write(text: string, stream: DaemonRenderStream): void { - this._writes.push([stream, text]); + this.#writes.push([stream, text]); } /** All stdout text written so far, concatenated. */ public get stdout(): string { - return this._collect('stdout'); + return this.#collect('stdout'); } /** All stderr text written so far, concatenated. */ public get stderr(): string { - return this._collect('stderr'); + return this.#collect('stderr'); } - private _collect(stream: DaemonRenderStream): string { - return this._writes + #collect(stream: DaemonRenderStream): string { + return this.#writes .filter(([s]: [DaemonRenderStream, string]) => s === stream) .map(([, text]: [DaemonRenderStream, string]) => text) .join(''); diff --git a/libraries/rushell/src/Parser.ts b/libraries/rushell/src/Parser.ts index c72f3f47914..3f80d944a69 100644 --- a/libraries/rushell/src/Parser.ts +++ b/libraries/rushell/src/Parser.ts @@ -6,26 +6,26 @@ import { type Tokenizer, type Token, TokenKind } from './Tokenizer'; import { type AstNode, AstScript, AstCommand, AstCompoundWord, AstText } from './AstNode'; export class Parser { - private readonly _tokenizer: Tokenizer; - private _peekedToken: Token | undefined; + readonly #tokenizer: Tokenizer; + #peekedToken: Token | undefined; public constructor(tokenizer: Tokenizer) { - this._tokenizer = tokenizer; - this._peekedToken = undefined; + this.#tokenizer = tokenizer; + this.#peekedToken = undefined; } public parse(): AstScript { const script: AstScript = new AstScript(); - const startingToken: Token = this._peekToken(); + const startingToken: Token = this.#peekToken(); - const astCommand: AstCommand | undefined = this._parseCommand(); + const astCommand: AstCommand | undefined = this.#parseCommand(); if (!astCommand) { throw new ParseError('Expecting a command', startingToken.range); } - const nextToken: Token = this._peekToken(); + const nextToken: Token = this.#peekToken(); if (nextToken.kind !== TokenKind.EndOfInput) { throw new ParseError(`Unexpected token: ${TokenKind[nextToken.kind]}`, nextToken.range); @@ -36,19 +36,19 @@ export class Parser { return script; } - private _parseCommand(): AstCommand | undefined { - this._skipWhitespace(); + #parseCommand(): AstCommand | undefined { + this.#skipWhitespace(); - const startingToken: Token = this._peekToken(); + const startingToken: Token = this.#peekToken(); const command: AstCommand = new AstCommand(); - command.commandPath = this._parseCompoundWord(); + command.commandPath = this.#parseCompoundWord(); if (!command.commandPath) { throw new ParseError('Expecting a command path', startingToken.range); } - while (this._skipWhitespace()) { - const compoundWord: AstCompoundWord | undefined = this._parseCompoundWord(); + while (this.#skipWhitespace()) { + const compoundWord: AstCompoundWord | undefined = this.#parseCompoundWord(); if (!compoundWord) { break; } @@ -58,11 +58,11 @@ export class Parser { return command; } - private _parseCompoundWord(): AstCompoundWord | undefined { + #parseCompoundWord(): AstCompoundWord | undefined { const compoundWord: AstCompoundWord = new AstCompoundWord(); for (;;) { - const node: AstNode | undefined = this._parseText(); + const node: AstNode | undefined = this.#parseText(); if (!node) { break; } @@ -77,11 +77,11 @@ export class Parser { return compoundWord; } - private _parseText(): AstText | undefined { - const token: Token = this._peekToken(); + #parseText(): AstText | undefined { + const token: Token = this.#peekToken(); if (token.kind === TokenKind.Text) { - this._readToken(); + this.#readToken(); const astText: AstText = new AstText(); astText.token = token; @@ -95,32 +95,32 @@ export class Parser { /** * Skips any whitespace tokens. Returns true if any whitespace was actually encountered. */ - private _skipWhitespace(): boolean { + #skipWhitespace(): boolean { let sawWhitespace: boolean = false; - while (this._peekToken().kind === TokenKind.Spaces) { - this._readToken(); + while (this.#peekToken().kind === TokenKind.Spaces) { + this.#readToken(); sawWhitespace = true; } - if (this._peekToken().kind === TokenKind.EndOfInput) { + if (this.#peekToken().kind === TokenKind.EndOfInput) { sawWhitespace = true; } return sawWhitespace; } - private _readToken(): Token { - if (this._peekedToken) { - const token: Token = this._peekedToken; - this._peekedToken = undefined; + #readToken(): Token { + if (this.#peekedToken) { + const token: Token = this.#peekedToken; + this.#peekedToken = undefined; return token; } else { - return this._tokenizer.readToken(); + return this.#tokenizer.readToken(); } } - private _peekToken(): Token { - if (!this._peekedToken) { - this._peekedToken = this._tokenizer.readToken(); + #peekToken(): Token { + if (!this.#peekedToken) { + this.#peekedToken = this.#tokenizer.readToken(); } - return this._peekedToken; + return this.#peekedToken; } } diff --git a/libraries/rushell/src/Rushell.ts b/libraries/rushell/src/Rushell.ts index 086b4127c10..ff89b09ae0d 100644 --- a/libraries/rushell/src/Rushell.ts +++ b/libraries/rushell/src/Rushell.ts @@ -31,37 +31,37 @@ export class Rushell { const parser: Parser = new Parser(tokenizer); const astScript: AstScript = parser.parse(); - return this._evaluateNode(astScript); + return this.#evaluateNode(astScript); } - private _evaluateNode(astNode: AstNode): IRushellExecuteResult { + #evaluateNode(astNode: AstNode): IRushellExecuteResult { switch (astNode.kind) { case AstKind.CompoundWord: - return { value: astNode.parts.map((x) => this._evaluateNode(x).value).join('') }; + return { value: astNode.parts.map((x) => this.#evaluateNode(x).value).join('') }; case AstKind.Text: return { value: astNode.token!.range.toString() }; case AstKind.Script: if (astNode.body) { - return this._evaluateNode(astNode.body); + return this.#evaluateNode(astNode.body); } break; case AstKind.Command: - return this._evaluateCommand(astNode); + return this.#evaluateCommand(astNode); default: throw new ParseError('Unsupported operation type: ' + astNode.kind, astNode.getFullRange()); } return { value: '' }; } - private _evaluateCommand(astCommand: AstCommand): IRushellExecuteResult { + #evaluateCommand(astCommand: AstCommand): IRushellExecuteResult { if (!astCommand.commandPath) { throw new ParseError('Missing command path', astCommand.getFullRange()); } - const commandPathResult: IRushellExecuteResult = this._evaluateNode(astCommand.commandPath); + const commandPathResult: IRushellExecuteResult = this.#evaluateNode(astCommand.commandPath); const commandArgResults: IRushellExecuteResult[] = []; for (let i: number = 0; i < astCommand.arguments.length; ++i) { - commandArgResults.push(this._evaluateNode(astCommand.arguments[i])); + commandArgResults.push(this.#evaluateNode(astCommand.arguments[i])); } const commandPath: string = commandPathResult.value; diff --git a/libraries/rushell/src/TextRange.ts b/libraries/rushell/src/TextRange.ts index b4fdc239884..5d06f17aa64 100644 --- a/libraries/rushell/src/TextRange.ts +++ b/libraries/rushell/src/TextRange.ts @@ -48,7 +48,7 @@ export class TextRange { this.buffer = buffer; this.pos = pos; this.end = end; - this._validateBounds(); + this.#validateBounds(); } /** @@ -163,7 +163,7 @@ export class TextRange { return { line, column }; } - private _validateBounds(): TextRange { + #validateBounds(): TextRange { if (this.pos < 0) { throw new Error('TextRange.pos cannot be negative'); } diff --git a/libraries/rushell/src/Tokenizer.ts b/libraries/rushell/src/Tokenizer.ts index 269f4937785..9aa3bbf6127 100644 --- a/libraries/rushell/src/Tokenizer.ts +++ b/libraries/rushell/src/Tokenizer.ts @@ -63,7 +63,7 @@ const variableCharacterRegExp: RegExp = /[a-z0-9_]/i; export class Tokenizer { public readonly input: TextRange; - private _currentIndex: number; + #currentIndex: number; public constructor(input: TextRange | string) { if (typeof input === 'string') { @@ -71,18 +71,18 @@ export class Tokenizer { } else { this.input = input; } - this._currentIndex = this.input.pos; + this.#currentIndex = this.input.pos; } public get currentIndex(): number { - return this._currentIndex; + return this.#currentIndex; } public readToken(): Token { const input: TextRange = this.input; - const startIndex: number = this._currentIndex; - const firstChar: string | undefined = this._peekCharacter(); + const startIndex: number = this.#currentIndex; + const firstChar: string | undefined = this.#peekCharacter(); // Reached end of input yet? if (firstChar === undefined) { @@ -91,44 +91,44 @@ export class Tokenizer { // Is it a sequence of whitespace? if (_isSpace(firstChar)) { - this._readCharacter(); + this.#readCharacter(); - while (_isSpace(this._peekCharacter())) { - this._readCharacter(); + while (_isSpace(this.#peekCharacter())) { + this.#readCharacter(); } - return new Token(TokenKind.Spaces, input.getNewRange(startIndex, this._currentIndex)); + return new Token(TokenKind.Spaces, input.getNewRange(startIndex, this.#currentIndex)); } // Is it a newline? if (firstChar === '\r') { - this._readCharacter(); - if (this._peekCharacter() === '\n') { - this._readCharacter(); + this.#readCharacter(); + if (this.#peekCharacter() === '\n') { + this.#readCharacter(); } - return new Token(TokenKind.NewLine, input.getNewRange(startIndex, this._currentIndex)); + return new Token(TokenKind.NewLine, input.getNewRange(startIndex, this.#currentIndex)); } else if (firstChar === '\n') { - this._readCharacter(); - return new Token(TokenKind.NewLine, input.getNewRange(startIndex, this._currentIndex)); + this.#readCharacter(); + return new Token(TokenKind.NewLine, input.getNewRange(startIndex, this.#currentIndex)); } // Is it a double-quoted string? if (firstChar === '"') { - this._readCharacter(); // consume the opening quote + this.#readCharacter(); // consume the opening quote let text: string = ''; - let c: string | undefined = this._peekCharacter(); + let c: string | undefined = this.#peekCharacter(); while (c !== '"') { if (c === undefined) { throw new ParseError( 'The double-quoted string is missing the ending quote', - input.getNewRange(startIndex, this._currentIndex) + input.getNewRange(startIndex, this.#currentIndex) ); } if (c === '\r' || c === '\n') { throw new ParseError( 'Newlines are not supported inside strings', - input.getNewRange(this._currentIndex, this._currentIndex + 1) + input.getNewRange(this.#currentIndex, this.#currentIndex + 1) ); } @@ -141,24 +141,24 @@ export class Tokenizer { // // NOTE: Dash interprets "\t" as a tab character, but Bash does not. if (c === '\\') { - this._readCharacter(); // discard the backslash - if (this._peekCharacter() === undefined) { + this.#readCharacter(); // discard the backslash + if (this.#peekCharacter() === undefined) { throw new ParseError( 'A backslash must be followed by another character', - input.getNewRange(this._currentIndex, this._currentIndex + 1) + input.getNewRange(this.#currentIndex, this.#currentIndex + 1) ); } // Add the escaped character - text += this._readCharacter(); + text += this.#readCharacter(); } else { - text += this._readCharacter(); + text += this.#readCharacter(); } - c = this._peekCharacter(); + c = this.#peekCharacter(); } - this._readCharacter(); // consume the closing quote + this.#readCharacter(); // consume the closing quote - return new Token(TokenKind.DoubleQuotedText, input.getNewRange(startIndex, this._currentIndex), text); + return new Token(TokenKind.DoubleQuotedText, input.getNewRange(startIndex, this.#currentIndex), text); } // Is it a text token? @@ -167,57 +167,57 @@ export class Tokenizer { let c: string | undefined = firstChar; do { if (c === '\\') { - this._readCharacter(); // discard the backslash - if (this._peekCharacter() === undefined) { + this.#readCharacter(); // discard the backslash + if (this.#peekCharacter() === undefined) { throw new ParseError( 'A backslash must be followed by another character', - input.getNewRange(this._currentIndex, this._currentIndex + 1) + input.getNewRange(this.#currentIndex, this.#currentIndex + 1) ); } // Add the escaped character - text += this._readCharacter(); + text += this.#readCharacter(); } else { - text += this._readCharacter(); + text += this.#readCharacter(); } - c = this._peekCharacter(); + c = this.#peekCharacter(); } while (c && textCharacterRegExp.test(c)); - return new Token(TokenKind.Text, input.getNewRange(startIndex, this._currentIndex), text); + return new Token(TokenKind.Text, input.getNewRange(startIndex, this.#currentIndex), text); } // Is it a dollar variable? The valid environment variable names are [A-Z_][A-Z0-9_]* if (firstChar === '$') { - this._readCharacter(); + this.#readCharacter(); - let name: string = this._readCharacter() || ''; + let name: string = this.#readCharacter() || ''; if (!startVariableCharacterRegExp.test(name)) { throw new ParseError( 'The "$" symbol must be followed by a letter or underscore', - input.getNewRange(startIndex, this._currentIndex) + input.getNewRange(startIndex, this.#currentIndex) ); } - let c: string | undefined = this._peekCharacter(); + let c: string | undefined = this.#peekCharacter(); while (c && variableCharacterRegExp.test(c)) { - name += this._readCharacter(); - c = this._peekCharacter(); + name += this.#readCharacter(); + c = this.#peekCharacter(); } - return new Token(TokenKind.DollarVariable, input.getNewRange(startIndex, this._currentIndex), name); + return new Token(TokenKind.DollarVariable, input.getNewRange(startIndex, this.#currentIndex), name); } // Is it the "&&" token? if (firstChar === '&') { - if (this._peekCharacterAfter() === '&') { - this._readCharacter(); - this._readCharacter(); - return new Token(TokenKind.AndIf, input.getNewRange(startIndex, this._currentIndex)); + if (this.#peekCharacterAfter() === '&') { + this.#readCharacter(); + this.#readCharacter(); + return new Token(TokenKind.AndIf, input.getNewRange(startIndex, this.#currentIndex)); } } // Otherwise treat it as an "other" character - this._readCharacter(); - return new Token(TokenKind.OtherCharacter, input.getNewRange(startIndex, this._currentIndex)); + this.#readCharacter(); + return new Token(TokenKind.OtherCharacter, input.getNewRange(startIndex, this.#currentIndex)); } public readTokens(): Token[] { @@ -234,33 +234,33 @@ export class Tokenizer { * Retrieve the next character in the input stream. * @returns a string of length 1, or undefined if the end of input is reached */ - private _readCharacter(): string | undefined { - if (this._currentIndex >= this.input.end) { + #readCharacter(): string | undefined { + if (this.#currentIndex >= this.input.end) { return undefined; } - return this.input.buffer[this._currentIndex++]; + return this.input.buffer[this.#currentIndex++]; } /** * Return the next character in the input stream, but don't advance the stream pointer. * @returns a string of length 1, or undefined if the end of input is reached */ - private _peekCharacter(): string | undefined { - if (this._currentIndex >= this.input.end) { + #peekCharacter(): string | undefined { + if (this.#currentIndex >= this.input.end) { return undefined; } - return this.input.buffer[this._currentIndex]; + return this.input.buffer[this.#currentIndex]; } /** * Return the character after the next character in the input stream, but don't advance the stream pointer. * @returns a string of length 1, or undefined if the end of input is reached */ - private _peekCharacterAfter(): string | undefined { - if (this._currentIndex + 1 >= this.input.end) { + #peekCharacterAfter(): string | undefined { + if (this.#currentIndex + 1 >= this.input.end) { return undefined; } - return this.input.buffer[this._currentIndex + 1]; + return this.input.buffer[this.#currentIndex + 1]; } } diff --git a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts index dd0b2ac40b0..c109fdc282f 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts @@ -38,7 +38,7 @@ interface IProjectLike { } export class BumpDecoupledLocalDependencies extends CommandLineAction { - private readonly _terminal: ITerminal; + readonly #terminal: ITerminal; public constructor(terminal: ITerminal) { super({ @@ -47,11 +47,11 @@ export class BumpDecoupledLocalDependencies extends CommandLineAction { documentation: '' }); - this._terminal = terminal; + this.#terminal = terminal; } protected override async onExecuteAsync(): Promise { - const terminal: ITerminal = this._terminal; + const terminal: ITerminal = this.#terminal; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation({ startingFolder: process.cwd() }); diff --git a/repo-scripts/repo-toolbox/src/cli/actions/CollectProjectFilesAction.ts b/repo-scripts/repo-toolbox/src/cli/actions/CollectProjectFilesAction.ts index 3ca761c423c..b8c57854af6 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/CollectProjectFilesAction.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/CollectProjectFilesAction.ts @@ -46,10 +46,10 @@ async function* _getFolderItemsRecursiveAsync( } export class CollectProjectFilesAction extends CommandLineAction { - private readonly _outputPathParameter: IRequiredCommandLineStringParameter; - private readonly _subfolderParameter: IRequiredCommandLineStringParameter; + readonly #outputPathParameter: IRequiredCommandLineStringParameter; + readonly #subfolderParameter: IRequiredCommandLineStringParameter; - private readonly _terminal: ITerminal; + readonly #terminal: ITerminal; public constructor(terminal: ITerminal) { super({ @@ -60,16 +60,16 @@ export class CollectProjectFilesAction extends CommandLineAction { ' deduplicates by relative path and content, and writes them to the output directory.' }); - this._terminal = terminal; + this.#terminal = terminal; - this._subfolderParameter = this.defineStringParameter({ + this.#subfolderParameter = this.defineStringParameter({ parameterLongName: '--subfolder', description: 'The subfolder within each project to collect files from (e.g. "temp/json-schemas").', argumentName: 'SUBFOLDER', required: true }); - this._outputPathParameter = this.defineStringParameter({ + this.#outputPathParameter = this.defineStringParameter({ parameterLongName: '--output-path', description: 'Path to the output directory for the collected files.', argumentName: 'PATH', @@ -78,11 +78,11 @@ export class CollectProjectFilesAction extends CommandLineAction { } protected override async onExecuteAsync(): Promise { - const terminal: ITerminal = this._terminal; + const terminal: ITerminal = this.#terminal; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation(); - const subfolder: string = this._subfolderParameter.value; - const outputPath: string = path.resolve(this._outputPathParameter.value); + const subfolder: string = this.#subfolderParameter.value; + const outputPath: string = path.resolve(this.#outputPathParameter.value); const contentByAbsolutePathByRelativePath: Map> = new Map(); diff --git a/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts b/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts index 13cf6bdf913..da437d1f15f 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts @@ -14,9 +14,9 @@ const GENERATED_PROJECT_SUMMARY_END_COMMENT_TEXT: string = '