diff --git a/.eslintrc.js b/.eslintrc.js index c3cac4161..013ae65b8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -194,7 +194,37 @@ module.exports = { cases: { camelCase: true, // pascalCase: true, }, - ignore: [/rn-extension\.ts/], + ignore: [ + /rn-extension\.ts/, + /CDPAPINames\.ts/, + /ConsoleLogger\.ts/, + /ICDPMessageHandler\.ts/, + /IExperiment\.ts/, + /IWDPHelper\.ts/, + /LogHelper\.ts/, + /NullLogger\.ts/, + /OutputChannelLogger\.ts/, + /RNTPreviewPrompt\.ts/, + /baseCDPMessageHandler\.ts/, + /configEASBuild\.ts/, + /graphQLFormatter\.ts/, + /hermesCDPMessageHandler\.ts/, + /iOSClienDevice\.ts/, + /iOSContainerUtility\.ts/, + /iOSDebugModeManager\.ts/, + /iOSDeviceTracker\.ts/, + /iOSDirectCDPMessageHandler\.ts/, + /iOSPlatform\.ts/, + /iOSTargetManager\.ts/, + /macOSDebugModeManager\.ts/, + /macOSPlatform\.ts/, + /nodeJS\.ts/, + /openEASProject\.ts/, + /openRNUpgradeHelper\.ts/, + /reactNativeCDPProxy\.ts/, + /reopenQRCode\.ts/, + /rnCDPMessageHandler\.ts/, + ], }, ], "unicorn/no-array-reduce": "warn", diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..ca79ca5b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/build-vsix.yml b/.github/workflows/build-vsix.yml index 1dbad40dc..0322c8cb8 100644 --- a/.github/workflows/build-vsix.yml +++ b/.github/workflows/build-vsix.yml @@ -20,9 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: "24" cache: npm @@ -44,7 +44,7 @@ jobs: gh release create "$GITHUB_REF_NAME" ./*.vsix --generate-notes fi - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: vsix path: "*.vsix" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..22461b155 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: "24" + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Bundle extension + run: npx gulp prod-build + + - name: Build + run: npm run build + + - name: Test + run: xvfb-run -a npm test + + - name: Localization tests + run: xvfb-run -a npm run test-localization diff --git a/src/cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler.ts b/src/cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler.ts index 02b5f748d..207310199 100644 --- a/src/cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler.ts +++ b/src/cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler.ts @@ -36,7 +36,8 @@ export class HermesCDPMessageHandler extends BaseCDPMessageHandler { private isFirstRun: boolean = true; private reloadTimer: NodeJS.Timeout | null = null; - public processDebuggerCDPMessage(event: any): ProcessedCDPMessage { + public processDebuggerCDPMessage(initialEvent: any): ProcessedCDPMessage { + let event = initialEvent; let sendBack = false; if (event.method === CDP_API_NAMES.CLOSE) { this.cancelFirstRunReload(); @@ -60,7 +61,8 @@ export class HermesCDPMessageHandler extends BaseCDPMessageHandler { }; } - public processApplicationCDPMessage(event: any): ProcessedCDPMessage { + public processApplicationCDPMessage(initialEvent: any): ProcessedCDPMessage { + let event = initialEvent; const sendBack = false; if (event.method === CDP_API_NAMES.DEBUGGER_PAUSED) { event = this.handlePausedEvent(event); diff --git a/src/cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler.ts b/src/cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler.ts index 565c86683..103526899 100644 --- a/src/cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler.ts +++ b/src/cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler.ts @@ -39,7 +39,8 @@ export class IOSDirectCDPMessageHandler extends BaseCDPMessageHandler { }; } - public processApplicationCDPMessage(event: any): ProcessedCDPMessage { + public processApplicationCDPMessage(initialEvent: any): ProcessedCDPMessage { + let event = initialEvent; if (event.method === CDP_API_NAMES.CONSOLE_MESSAGE_ADDED) { event = this.processDeprecatedConsoleMessage(event); } diff --git a/src/cdp-proxy/debuggerEndpointHelper.ts b/src/cdp-proxy/debuggerEndpointHelper.ts index 397ac22c8..a1dcfc426 100644 --- a/src/cdp-proxy/debuggerEndpointHelper.ts +++ b/src/cdp-proxy/debuggerEndpointHelper.ts @@ -34,22 +34,26 @@ export class DebuggerEndpointHelper { */ public async retryGetWSEndpoint( browserURL: string, - attemptNumber: number, + initialAttemptNumber: number, cancellationToken: CancellationToken, isHermes: boolean = false, settingsPort?: number, ): Promise { + let attemptNumber = initialAttemptNumber; while (true) { try { let url = ""; if (settingsPort) { url = `http://localhost:${settingsPort}`; try { + // eslint-disable-next-line no-await-in-loop -- sequential retry loop return await this.getWSEndpoint(browserURL, isHermes); } catch { + // eslint-disable-next-line no-await-in-loop -- sequential retry loop return await this.getWSEndpoint(url, isHermes); } } else { + // eslint-disable-next-line no-await-in-loop -- sequential retry loop return await this.getWSEndpoint(browserURL, isHermes); } } catch (err) { @@ -71,6 +75,7 @@ export class DebuggerEndpointHelper { } attemptNumber--; + // eslint-disable-next-line no-await-in-loop -- delay between retry attempts await PromiseUtil.delay(700); } } diff --git a/src/cdp-proxy/reactNativeCDPProxy.ts b/src/cdp-proxy/reactNativeCDPProxy.ts index e5e067db4..12dd07316 100644 --- a/src/cdp-proxy/reactNativeCDPProxy.ts +++ b/src/cdp-proxy/reactNativeCDPProxy.ts @@ -112,17 +112,15 @@ export class ReactNativeCDPProxy { this.debuggerTarget.pause(); // don't listen for events until the target is ready if (!this.browserInspectUri) { - if (this.cancellationToken) { - this.browserInspectUri = await this.debuggerEndpointHelper.retryGetWSEndpoint( - `http://localhost:${this.applicationTargetPort}`, - 90, - this.cancellationToken, - ); - } else { - this.browserInspectUri = await this.debuggerEndpointHelper.getWSEndpoint( - `http://localhost:${this.applicationTargetPort}`, - ); - } + this.browserInspectUri = this.cancellationToken + ? await this.debuggerEndpointHelper.retryGetWSEndpoint( + `http://localhost:${this.applicationTargetPort}`, + 90, + this.cancellationToken, + ) + : await this.debuggerEndpointHelper.getWSEndpoint( + `http://localhost:${this.applicationTargetPort}`, + ); } this.applicationTarget = new Connection(await this.createApplicationTransport()); diff --git a/src/common/entryPointHandler.ts b/src/common/entryPointHandler.ts index 38509b38b..a63d820cb 100644 --- a/src/common/entryPointHandler.ts +++ b/src/common/entryPointHandler.ts @@ -76,14 +76,18 @@ export class EntryPointHandler { } private handleErrors( - error: InternalError, + internalError: InternalError, resultOfCode: Promise, errorsAreFatal: boolean, ): Promise { resultOfCode.catch(reason => { const isDebugeeProcess = this.processType === ProcessType.Debugee; const shouldLogStack = !errorsAreFatal || isDebugeeProcess; - this.logger.error(error.message, ErrorHelper.wrapError(error, reason), shouldLogStack); + this.logger.error( + internalError.message, + ErrorHelper.wrapError(internalError, reason), + shouldLogStack, + ); // For the debugee process we don't want to throw an exception because the debugger // will appear to the user if he turned on the VS Code uncaught exceptions feature. if (errorsAreFatal) { diff --git a/src/common/installHelper.ts b/src/common/installHelper.ts index 68380ab5b..1c92bd77b 100644 --- a/src/common/installHelper.ts +++ b/src/common/installHelper.ts @@ -7,7 +7,10 @@ import { SimctrlHelper } from "../extension/ios/simctl"; import { OutputChannelLogger } from "../extension/log/OutputChannelLogger"; import { ChildProcess } from "./node/childProcess"; -export async function installAndroidApplication(project: AppLauncher, appPath: string) { +export async function installAndroidApplication( + project: AppLauncher, + appPath: string, +): Promise { const logger = OutputChannelLogger.getMainChannel(); const adbHelper = new AdbHelper( project.getPackager().getProjectPath(), @@ -15,7 +18,7 @@ export async function installAndroidApplication(project: AppLauncher, appPath: s ); const targets = await adbHelper.getOnlineTargets(); - if (targets.length == 0) { + if (targets.length === 0) { throw new Error("No online target found, please check your emulator status."); } else if (targets.length > 1) { logger.logStream( @@ -37,7 +40,7 @@ export async function installAndroidApplication(project: AppLauncher, appPath: s } } -export async function installiOSApplication(project: AppLauncher, appPath: string) { +export async function installiOSApplication(project: AppLauncher, appPath: string): Promise { const logger = OutputChannelLogger.getMainChannel(); const childProcess: ChildProcess = new ChildProcess(); @@ -54,7 +57,7 @@ export async function installiOSApplication(project: AppLauncher, appPath: strin throw e; } - if (targets.length == 1) { + if (targets.length === 1) { throw new Error("No booted iOS simulator found, please check your simulator status."); } else if (targets.length > 2) { logger.logStream( diff --git a/src/common/node/childProcess.ts b/src/common/node/childProcess.ts index 32f460cee..bf1877ca5 100644 --- a/src/common/node/childProcess.ts +++ b/src/common/node/childProcess.ts @@ -58,12 +58,7 @@ export class ChildProcess { process = this.childProcess.exec( command, options, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ( - error: nodeChildProcess.ExecException | null, - stdout: string | Buffer, - stderr: string | Buffer, - ) => { + (error: nodeChildProcess.ExecException | null, stdout: string | Buffer) => { if (error) { reject( ErrorHelper.getNestedError( @@ -98,11 +93,7 @@ export class ChildProcess { command, args, options, - ( - error: nodeChildProcess.ExecException | null, - stdout: string | Buffer, - stderr: string | Buffer, - ) => { + (error: nodeChildProcess.ExecException | null, stdout: string | Buffer) => { if (error) { reject( ErrorHelper.getNestedError( diff --git a/src/common/node/promise.ts b/src/common/node/promise.ts index fa7f06041..500857b95 100644 --- a/src/common/node/promise.ts +++ b/src/common/node/promise.ts @@ -49,10 +49,10 @@ export class PromiseUtil { ): Promise { const arraySources: T[] = sources instanceof Promise ? await sources : sources; - return arraySources.reduce(async (previousReduction: Promise, newSource: T) => { - await previousReduction; - return generateAsyncOperation(newSource); - }, Promise.resolve()); + for (const source of arraySources) { + // eslint-disable-next-line no-await-in-loop -- operations run sequentially by design + await generateAsyncOperation(source); + } } public static async delay(duration: number): Promise { @@ -66,7 +66,7 @@ export class PromiseUtil { interval: number = 1000, timeout?: number, ): Promise { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { let rejectTimeout: NodeJS.Timeout | undefined; // eslint-disable-next-line prefer-const let сheckInterval: NodeJS.Timeout | undefined; @@ -103,14 +103,14 @@ export class PromiseUtil { } }; - const resolved = await tryToResolve(); - if (resolved) { - return; - } - - сheckInterval = setInterval(async () => { - await tryToResolve(); - }, interval); + void tryToResolve().then(resolved => { + if (resolved) { + return; + } + сheckInterval = setInterval(async () => { + await tryToResolve(); + }, interval); + }); }); } diff --git a/src/common/nodeHelper.ts b/src/common/nodeHelper.ts index 5effd87ab..52c35d338 100644 --- a/src/common/nodeHelper.ts +++ b/src/common/nodeHelper.ts @@ -3,7 +3,7 @@ import { ChildProcess } from "./node/childProcess"; -export async function getNodeVersion(projectPath: string, env: object) { +export async function getNodeVersion(projectPath: string, env: object): Promise { try { return await new ChildProcess().execToString("node -v", { cwd: projectPath, env }); } catch (error) { diff --git a/src/common/packageLoader.ts b/src/common/packageLoader.ts index d3a1daa8e..cdd20e268 100644 --- a/src/common/packageLoader.ts +++ b/src/common/packageLoader.ts @@ -175,6 +175,7 @@ export class PackageLoader { const load = this.packagesQueue.length; const packagesForInstall = this.packagesQueue.slice(0, load); + // eslint-disable-next-line no-await-in-loop -- packages are installed one npm command at a time await commandExecutor.spawnWithProgress( HostPlatform.getNpmCliCommand("npm"), ["install", ...packagesForInstall, "--verbose", "--no-save", "--global-style"], @@ -184,7 +185,8 @@ export class PackageLoader { ); // Try to require all pending packages after every 'npm install ...' command const requiresToRemove: ((load?: string[]) => Promise)[] = []; - for (tryToRequire of this.requireQueue) { + for (const tryToRequire of this.requireQueue) { + // eslint-disable-next-line no-await-in-loop -- iterates a live queue that may grow while awaiting if (await tryToRequire(packagesForInstall)) { requiresToRemove.push(tryToRequire); } diff --git a/src/common/packager.ts b/src/common/packager.ts index e69168629..ff5a89f7c 100644 --- a/src/common/packager.ts +++ b/src/common/packager.ts @@ -273,13 +273,16 @@ export class Packager { ); let packagerSpawnResult; - if (this.runOptions?.platform != "exponent" && this.runOptions?.platform != "expoweb") { + if ( + this.runOptions?.platform !== "exponent" && + this.runOptions?.platform !== "expoweb" + ) { packagerSpawnResult = new CommandExecutor( nodeModulesRoot, this.projectPath, this.logger, ).spawnReactPackager(args, spawnOptions); - } else if (this.runOptions?.platform == "exponent") { + } else if (this.runOptions?.platform === "exponent") { this.runOptions?.expoPlatformType && args.push(`--${this.runOptions?.expoPlatformType?.toLowerCase()}`); logger.log( diff --git a/src/common/projectVersionHelper.ts b/src/common/projectVersionHelper.ts index 1c9715d7c..298bc51e1 100644 --- a/src/common/projectVersionHelper.ts +++ b/src/common/projectVersionHelper.ts @@ -56,8 +56,9 @@ export class ProjectVersionHelper { */ public static async getReactNativeRequiredNodeRange( projectRoot: string, - nodeModulesRoot?: string, + initialNodeModulesRoot?: string, ): Promise { + let nodeModulesRoot = initialNodeModulesRoot; try { if (!nodeModulesRoot) { nodeModulesRoot = @@ -88,8 +89,9 @@ export class ProjectVersionHelper { public static async getReactNativeVersions( projectRoot: string, additionalPackagesToCheck?: ParsedPackage[], - nodeModulesRoot?: string, + initialNodeModulesRoot?: string, ): Promise { + let nodeModulesRoot = initialNodeModulesRoot; if (!nodeModulesRoot) { nodeModulesRoot = ProjectsStorage.getFolderByProjectRootPath( @@ -113,8 +115,9 @@ export class ProjectVersionHelper { public static async tryToGetRNSemverValidVersionsFromProjectPackage( projectRoot: string, additionalPackagesToCheck?: ParsedPackage[], - nodeModulesRoot?: string, + initialNodeModulesRoot?: string, ): Promise { + let nodeModulesRoot = initialNodeModulesRoot; const versions = await ProjectVersionHelper.getReactNativeVersionsFromProjectPackage( projectRoot, additionalPackagesToCheck, @@ -186,11 +189,7 @@ export class ProjectVersionHelper { }); const packageVersionArray = await Promise.all(versionPromises); - const packageVersions = packageVersionArray.reduce( - (allPackageVersions, packageVersion) => - Object.assign(allPackageVersions, packageVersion), - {}, - ); + const packageVersions: PackageVersion = Object.assign({}, ...packageVersionArray); if (ProjectVersionHelper.isVersionError(packageVersions["react-native"])) { throw ErrorHelper.getInternalError(InternalErrorCode.ReactNativePackageIsNotInstalled); } diff --git a/src/common/reactNativeProjectHelper.ts b/src/common/reactNativeProjectHelper.ts index 129511be8..c1bb0545d 100644 --- a/src/common/reactNativeProjectHelper.ts +++ b/src/common/reactNativeProjectHelper.ts @@ -101,7 +101,7 @@ export class ReactNativeProjectHelper { return hermesEnabled; } - public static async UpdateMertoBundlerForExpoWeb(launchArgs: any) { + public static async UpdateMertoBundlerForExpoWeb(launchArgs: any): Promise { const appJsonPath = path.join(launchArgs.cwd, "app.json"); const fs = new FileSystem(); const appJson = await fs.readFile(appJsonPath).then(content => { @@ -114,7 +114,7 @@ export class ReactNativeProjectHelper { } } - public static async verifyMetroConfigFile(projectRoot: string) { + public static async verifyMetroConfigFile(projectRoot: string): Promise { const logger = OutputChannelLogger.getChannel(OutputChannelLogger.MAIN_CHANNEL_NAME, true); let version; diff --git a/src/common/telemetryHelper.ts b/src/common/telemetryHelper.ts index 1cc4945b5..854112c8d 100644 --- a/src/common/telemetryHelper.ts +++ b/src/common/telemetryHelper.ts @@ -60,8 +60,9 @@ export class TelemetryHelper { public static addPlatformPropertiesToTelemetryProperties( args: IRunOptions, versions: RNPackageVersions, - properties: ICommandTelemetryProperties, + initialProperties: ICommandTelemetryProperties, ): any { + let properties = initialProperties; properties = TelemetryHelper.addPropertyToTelemetryProperties( versions.reactNativeVersion, "reactNativeVersion", @@ -286,11 +287,11 @@ export class TelemetryHelper { propertyValue: any, isPii: boolean, ): void { - for (let i = 0; i < propertyValue.length; i++) { + for (const [i, value] of propertyValue.entries()) { TelemetryHelper.setTelemetryEventProperty( event, propertyName + String(i), - propertyValue[i], + value, isPii, ); } diff --git a/src/common/utils.ts b/src/common/utils.ts index 0084ecebd..394f19d41 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -46,9 +46,9 @@ export function areSameDates(date1: Date, date2: Date): boolean { ); } -export function getRandomIntInclusive(min: number, max: number): number { - min = Math.ceil(min); - max = Math.floor(max); +export function getRandomIntInclusive(initialMin: number, initialMax: number): number { + const min = Math.ceil(initialMin); + const max = Math.floor(initialMax); return Math.floor(Math.random() * (max - min + 1)) + min; } @@ -127,7 +127,7 @@ export function ipToBuffer(ip: string): Buffer { throw new Error("Invalid IP address format."); } -export async function switchBundleOptions(projectRootPath: string, flag: boolean) { +export async function switchBundleOptions(projectRootPath: string, flag: boolean): Promise { const splitBundleOptionsPath = path.resolve( projectRootPath, "node_modules", diff --git a/src/debugger/debugSessionBase.ts b/src/debugger/debugSessionBase.ts index a7a1858b2..6f2b6f18a 100644 --- a/src/debugger/debugSessionBase.ts +++ b/src/debugger/debugSessionBase.ts @@ -215,7 +215,7 @@ export abstract class DebugSessionBase extends LoggingDebugSession { ); } const settingsPort = this.appLauncher.getPackagerPort(projectRootPath); - if (this.appLauncher.getPackager().getPort() != settingsPort) { + if (this.appLauncher.getPackager().getPort() !== settingsPort) { this.appLauncher.getPackager().resetToSettingsPort(); } } diff --git a/src/debugger/direct/directDebugSession.ts b/src/debugger/direct/directDebugSession.ts index e8e9f5dce..d1deb8262 100644 --- a/src/debugger/direct/directDebugSession.ts +++ b/src/debugger/direct/directDebugSession.ts @@ -79,7 +79,7 @@ export class DirectDebugSession extends DebugSessionBase { try { try { - if (launchArgs.platform != "exponent") { + if (launchArgs.platform !== "exponent") { await ReactNativeProjectHelper.verifyMetroConfigFile(launchArgs.cwd); } await this.initializeSettings(launchArgs); @@ -184,11 +184,12 @@ export class DirectDebugSession extends DebugSessionBase { // eslint-disable-next-line @typescript-eslint/no-unused-vars await TelemetryHelper.generate("attach", extProps, async generator => { - const port = attachArgs.useHermesEngine - ? attachArgs.port || this.appLauncher.getPackagerPort(attachArgs.cwd) - : attachArgs.platform === PlatformType.iOS - ? attachArgs.port || IWDPHelper.iOS_WEBKIT_DEBUG_PROXY_DEFAULT_PORT - : null; + let port: number | null = null; + if (attachArgs.useHermesEngine) { + port = attachArgs.port || this.appLauncher.getPackagerPort(attachArgs.cwd); + } else if (attachArgs.platform === PlatformType.iOS) { + port = attachArgs.port || IWDPHelper.iOS_WEBKIT_DEBUG_PROXY_DEFAULT_PORT; + } if (port === null) { throw ErrorHelper.getInternalError( InternalErrorCode.CouldNotDirectDebugWithoutHermesEngine, @@ -199,11 +200,12 @@ export class DirectDebugSession extends DebugSessionBase { logger.log(`Connecting to ${attachArgs.port} port`); await this.appLauncher.getRnCdpProxy().stopServer(); - const cdpMessageHandler: BaseCDPMessageHandler | null = attachArgs.useHermesEngine - ? new HermesCDPMessageHandler() - : attachArgs.platform === PlatformType.iOS - ? new IOSDirectCDPMessageHandler() - : null; + let cdpMessageHandler: BaseCDPMessageHandler | null = null; + if (attachArgs.useHermesEngine) { + cdpMessageHandler = new HermesCDPMessageHandler(); + } else if (attachArgs.platform === PlatformType.iOS) { + cdpMessageHandler = new IOSDirectCDPMessageHandler(); + } if (!cdpMessageHandler) { throw ErrorHelper.getInternalError( @@ -262,7 +264,7 @@ export class DirectDebugSession extends DebugSessionBase { const debuggerType = await this.debuggerEndpointHelper.getDebuggerTpye( `http://localhost:${attachArgs.port}`, ); - if (debuggerType == "expo") { + if (debuggerType === "expo") { const expoBrowserInspectUri = `${browserInspectUri.split("&")[0]}&page=2`; this.appLauncher.getRnCdpProxy().setBrowserInspectUri(expoBrowserInspectUri); } else { @@ -291,7 +293,7 @@ export class DirectDebugSession extends DebugSessionBase { this.debugSessionStatus = DebugSessionStatus.Stopping; // Stop packager when using expo-cli, to avoid launch conflicts in the next launch request - if (this.appLauncher.getPackager().getPlatform() == "exponent") { + if (this.appLauncher.getPackager().getPlatform() === "exponent") { await this.appLauncher.getPackager().stop(); } diff --git a/src/debugger/forkedAppWorker.ts b/src/debugger/forkedAppWorker.ts index 691599e15..8c6836798 100644 --- a/src/debugger/forkedAppWorker.ts +++ b/src/debugger/forkedAppWorker.ts @@ -136,7 +136,8 @@ export class ForkedAppWorker implements IDebuggeeWorker { return port; } - public async postMessage(rnMessage: RNAppMessage): Promise { + public async postMessage(rawMessage: RNAppMessage): Promise { + let rnMessage = rawMessage; // Before sending messages, make sure that the worker is loaded await new Promise(resolve => { if (this.workerLoaded) { diff --git a/src/debugger/jsDebugConfigAdapter.ts b/src/debugger/jsDebugConfigAdapter.ts index 00e3f84b3..e81a0c481 100644 --- a/src/debugger/jsDebugConfigAdapter.ts +++ b/src/debugger/jsDebugConfigAdapter.ts @@ -30,7 +30,7 @@ export class JsDebugConfigAdapter { attachArgs: IAttachRequestArgs, cdpProxyPort: number, sessionId: string, - ) { + ): Promise { const extraArgs: any = {}; const versions = await ProjectVersionHelper.getReactNativeVersions(attachArgs.cwd); // Handle project file path from 0.76 diff --git a/src/debugger/rnDebugSession.ts b/src/debugger/rnDebugSession.ts index 4227caba8..9deb0d474 100644 --- a/src/debugger/rnDebugSession.ts +++ b/src/debugger/rnDebugSession.ts @@ -59,7 +59,7 @@ export class RNDebugSession extends DebugSessionBase { ): Promise { try { try { - if (launchArgs.platform != "exponent") { + if (launchArgs.platform !== "exponent") { await ReactNativeProjectHelper.verifyMetroConfigFile(launchArgs.cwd); } const requiredNodeRange = diff --git a/src/debugger/sourceMap.ts b/src/debugger/sourceMap.ts index f9f4bba02..fc026b7da 100644 --- a/src/debugger/sourceMap.ts +++ b/src/debugger/sourceMap.ts @@ -103,7 +103,8 @@ export class SourceMapUtil { } } - public appendSourceMapPaths(scriptBody: string, sourceMappingUrl: string): string { + public appendSourceMapPaths(initialScriptBody: string, sourceMappingUrl: string): string { + let scriptBody = initialScriptBody; scriptBody += `//# sourceMappingURL=${sourceMappingUrl}`; return scriptBody; } @@ -176,11 +177,14 @@ export class SourceMapUtil { * 2. It changes the path separators to Unix style. */ private updateSourceMapPath( - sourcePath: string, + initialSourcePath: string, sourcesRootPath: string, - packagerRemoteRoot?: string, - packagerLocalRoot?: string, + initialPackagerRemoteRoot?: string, + initialPackagerLocalRoot?: string, ) { + let sourcePath = initialSourcePath; + let packagerRemoteRoot = initialPackagerRemoteRoot; + let packagerLocalRoot = initialPackagerLocalRoot; if (packagerRemoteRoot && packagerLocalRoot) { packagerRemoteRoot = this.makeUnixStylePath(packagerRemoteRoot); packagerLocalRoot = this.makeUnixStylePath(packagerLocalRoot); diff --git a/src/debugger/sourceMapsCombinator.ts b/src/debugger/sourceMapsCombinator.ts index 76ac031c0..7208ca7c9 100644 --- a/src/debugger/sourceMapsCombinator.ts +++ b/src/debugger/sourceMapsCombinator.ts @@ -22,17 +22,14 @@ const DISK_LETTER_RE = /^(?:[a-z]{2,}:\/{3})?[a-z]:/i; export class SourceMapsCombinator { public convert(rawBundleSourcemap: RawSourceMap): RawSourceMap { // Find user files from bundle files list - const consumers: { [key: string]: SourceMapConsumer } = rawBundleSourcemap.sources.reduce( - (result: { [key: string]: SourceMapConsumer }, file) => { - // Skip files inside node_modules - if (file.includes("node_modules")) return result; - - const consumer: SourceMapConsumer | null = this.getSourceMapConsumerFrom(file); - if (consumer) result[file] = consumer; - return result; - }, - {}, - ); + const consumers: { [key: string]: SourceMapConsumer } = {}; + for (const file of rawBundleSourcemap.sources) { + // Skip files inside node_modules + if (file.includes("node_modules")) continue; + + const consumer: SourceMapConsumer | null = this.getSourceMapConsumerFrom(file); + if (consumer) consumers[file] = consumer; + } if (Object.keys(consumers).length === 0) { // Sourcemaps not found, so return original bundle sourcemap diff --git a/src/debugger/webDebugSession.ts b/src/debugger/webDebugSession.ts index c0e89d82b..ea71b14ff 100644 --- a/src/debugger/webDebugSession.ts +++ b/src/debugger/webDebugSession.ts @@ -68,7 +68,7 @@ export class WebDebugSession extends DebugSessionBase { logger.verbose(`Launching the application: ${JSON.stringify(launchArgs, null, 2)}`); await this.updateWebpackMetroConfig(launchArgs); - await this.verifyExpoWebRequiredDependencies(launchArgs); + this.verifyExpoWebRequiredDependencies(launchArgs); await this.appLauncher.launchExpoWeb(launchArgs); await this.waitExpoWebIsRunning(launchArgs); await this.appLauncher.launchBrowser(launchArgs); @@ -89,7 +89,6 @@ export class WebDebugSession extends DebugSessionBase { protected async attachRequest( response: DebugProtocol.AttachResponse, attachArgs: IAttachRequestArgs, - request?: DebugProtocol.Request, ): Promise { const doAttach = async (attachArgs: IAttachRequestArgs) => { try { @@ -128,7 +127,7 @@ export class WebDebugSession extends DebugSessionBase { } }, ); - await this.establishDebugSession(processedAttachArgs); + this.establishDebugSession(processedAttachArgs); this.debugSessionStatus = DebugSessionStatus.ConnectionDone; }); @@ -162,10 +161,7 @@ export class WebDebugSession extends DebugSessionBase { return super.disconnectRequest(response, args, request); } - protected establishDebugSession( - attachArgs: IAttachRequestArgs, - resolve?: (value?: void | PromiseLike | undefined) => void, - ): void { + protected establishDebugSession(attachArgs: IAttachRequestArgs): void { if (this.cdpProxy) { const attachArguments = JsDebugConfigAdapter.createChromeDebuggingConfig( attachArgs, diff --git a/src/extension/android/adb.ts b/src/extension/android/adb.ts index 2fc99fbdd..d51b2cddf 100644 --- a/src/extension/android/adb.ts +++ b/src/extension/android/adb.ts @@ -279,7 +279,7 @@ export class AdbHelper { const result: IDebuggableMobileTarget[] = []; const regex = new RegExp("^(\\S+)\\t(\\S+)$", "mg"); let match = regex.exec(input); - while (match != null) { + while (match !== null) { result.push({ id: match[1], isOnline: match[2] === "device", diff --git a/src/extension/android/androidContainerUtility.ts b/src/extension/android/androidContainerUtility.ts index 05d6b4502..f25813a26 100644 --- a/src/extension/android/androidContainerUtility.ts +++ b/src/extension/android/androidContainerUtility.ts @@ -41,7 +41,7 @@ class RunAsError extends Error { * @format */ -export function push( +export async function push( adbHelper: AdbHelper, deviceId: string, app: string, @@ -49,27 +49,22 @@ export function push( contents: string, logger?: OutputChannelLogger, ): Promise { - return validateAppName(app).then(validApp => - validateFilePath(filepath).then(validFilepath => - validateFileContent(contents).then(validContent => - _push(adbHelper, deviceId, validApp, validFilepath, validContent, logger), - ), - ), - ); + const validApp = await validateAppName(app); + const validFilepath = await validateFilePath(filepath); + const validContent = await validateFileContent(contents); + return _push(adbHelper, deviceId, validApp, validFilepath, validContent, logger); } -export function pull( +export async function pull( adbHelper: AdbHelper, deviceId: string, app: string, path: string, logger?: OutputChannelLogger, ): Promise { - return validateAppName(app).then(validApp => - validateFilePath(path).then(validPath => - _pull(adbHelper, deviceId, validApp, validPath, logger), - ), - ); + const validApp = await validateAppName(app); + const validPath = await validateFilePath(path); + return _pull(adbHelper, deviceId, validApp, validPath, logger); } /** @@ -152,7 +147,7 @@ function validateFileContent(content: string): Promise { return Promise.reject(new Error(`Disallowed escaping file content: ${content}`)); } -function _push( +async function _push( adbHelper: AdbHelper, deviceId: string, app: string, @@ -161,25 +156,25 @@ function _push( logger?: OutputChannelLogger, ): Promise { const command = `echo \\"${contents}\\" > "${filename}" && chmod 644 "${filename}"`; - return executeCommandAsApp(adbHelper, deviceId, app, command) - .then(res => { - logger?.debug(res); - }) - .catch(error => { - if (error instanceof RunAsError) { - // Fall back to running the command directly. This will work if adb is running as root. - return executeCommandWithSu(adbHelper, deviceId, app, command) - .then(() => undefined) - .catch(e => { - logger?.debug(e.toString()); - throw error; - }); + try { + const res = await executeCommandAsApp(adbHelper, deviceId, app, command); + logger?.debug(res); + } catch (error) { + if (error instanceof RunAsError) { + // Fall back to running the command directly. This will work if adb is running as root. + try { + await executeCommandWithSu(adbHelper, deviceId, app, command); + return; + } catch (e) { + logger?.debug(String(e)); + throw error; } - throw error; - }); + } + throw error; + } } -function _pull( +async function _pull( adbHelper: AdbHelper, deviceId: string, app: string, @@ -187,17 +182,21 @@ function _pull( logger?: OutputChannelLogger, ): Promise { const command = `cat "${path}"`; - return executeCommandAsApp(adbHelper, deviceId, app, command).catch(error => { + try { + return await executeCommandAsApp(adbHelper, deviceId, app, command); + } catch (error) { if (error instanceof RunAsError) { // Fall back to running the command directly. This will work if adb is running as root. - return executeCommandWithSu(adbHelper, deviceId, app, command).catch(e => { + try { + return await executeCommandWithSu(adbHelper, deviceId, app, command); + } catch (e) { // Throw the original error. - logger?.debug(e.toString()); + logger?.debug(String(e)); throw error; - }); + } } throw error; - }); + } } // Keep this method private since it relies on pre-validated arguments diff --git a/src/extension/android/androidDeviceTracker.ts b/src/extension/android/androidDeviceTracker.ts index 6fb8d5da7..f64e858a8 100644 --- a/src/extension/android/androidDeviceTracker.ts +++ b/src/extension/android/androidDeviceTracker.ts @@ -44,6 +44,7 @@ export class AndroidDeviceTracker extends AbstractDeviceTracker { onlineDevice.isVirtualTarget, ClientOS.Android, ); + // eslint-disable-next-line no-await-in-loop -- adb reverse is set up per device sequentially; keeps fail-fast and insertion order await this.initAndroidDevice(androidDevice); DeviceStorage.devices.set(androidDevice.id, androidDevice); } diff --git a/src/extension/android/androidTargetManager.ts b/src/extension/android/androidTargetManager.ts index 5954e4fb2..18dfb8f50 100644 --- a/src/extension/android/androidTargetManager.ts +++ b/src/extension/android/androidTargetManager.ts @@ -119,6 +119,7 @@ export class AndroidTargetManager extends MobileTargetManager { const onlineTargets = await this.adbHelper.getOnlineTargets(); for (const device of onlineTargets) { if (device.isVirtualTarget && collectSimulators) { + // eslint-disable-next-line no-await-in-loop -- adb is queried per device sequentially const avdName = await this.adbHelper.getAvdNameById(device.id); const emulatorTarget = targetList.find(target => target.name === avdName); if (emulatorTarget) { @@ -153,7 +154,7 @@ export class AndroidTargetManager extends MobileTargetManager { emulatorProcess.outcome.catch(error => { emulatorLaunchFailed = true; if ( - process.platform == "win32" && + process.platform === "win32" && process.env.SESSIONNAME && process.env.SESSIONNAME.toLowerCase().includes("rdp-tcp") ) { @@ -175,6 +176,7 @@ export class AndroidTargetManager extends MobileTargetManager { throw new Error("Android emulator launch failed unexpectedly"); const connectedDevices = await this.adbHelper.getOnlineTargets(); for (const target of connectedDevices) { + // eslint-disable-next-line no-await-in-loop -- polling condition, returns on first match const onlineAvdName = await this.adbHelper.getAvdNameById(target.id); if (onlineAvdName === emulatorTarget.name) { return target.id; diff --git a/src/extension/appLauncher.ts b/src/extension/appLauncher.ts index d0f786af4..e28f0c923 100644 --- a/src/extension/appLauncher.ts +++ b/src/extension/appLauncher.ts @@ -519,7 +519,7 @@ export class AppLauncher { // Launch browser let browserFinder: BrowserHelper.IBrowserFinder; - if (launchArgs.platform == PlatformType.ExpoWeb) { + if (launchArgs.platform === PlatformType.ExpoWeb) { switch (launchArgs.browserTarget) { case BROWSER_TYPES.Edge: browserFinder = new BrowserHelper.EdgeBrowserFinder( @@ -563,9 +563,9 @@ export class AppLauncher { public getRunArguments(launchArgs: any): string[] { let userDataDir; - if (launchArgs.browserTarget == BROWSER_TYPES.Chrome) { + if (launchArgs.browserTarget === BROWSER_TYPES.Chrome) { userDataDir = path.join(HostPlatform.getSettingsHome(), AppLauncher.CHROME_DATA_DIR); - } else if (launchArgs.browserTarget == BROWSER_TYPES.Edge) { + } else if (launchArgs.browserTarget === BROWSER_TYPES.Edge) { userDataDir = path.join(HostPlatform.getSettingsHome(), AppLauncher.EDGE_DATA_DIR); } else { userDataDir = ""; diff --git a/src/extension/commands/cleanRestartPackager.ts b/src/extension/commands/cleanRestartPackager.ts index dae1b2baa..6ba1534c7 100644 --- a/src/extension/commands/cleanRestartPackager.ts +++ b/src/extension/commands/cleanRestartPackager.ts @@ -71,6 +71,7 @@ export class CleanRestartPackager extends ReactNativeCommand { if (match && match[1]) { const pid = match[1]; logger.info(`Found Metro process with PID: ${pid}`); + // eslint-disable-next-line no-await-in-loop -- kill PIDs one at a time; taskkill /T kills process trees, so later PIDs may already be gone await childProcess .exec(`taskkill /PID ${pid} /F /T`) .then(killResult => killResult.outcome); @@ -143,6 +144,7 @@ export class CleanRestartPackager extends ReactNativeCommand { const stat = fs.statSync(filePath); if (stat.isDirectory()) { + // eslint-disable-next-line no-await-in-loop -- entries are removed sequentially before rmdir await this.deleteDirectory(filePath); } else { fs.unlinkSync(filePath); diff --git a/src/extension/commands/installExpoGoApplication.ts b/src/extension/commands/installExpoGoApplication.ts index c7e03110f..c6c47c1e1 100644 --- a/src/extension/commands/installExpoGoApplication.ts +++ b/src/extension/commands/installExpoGoApplication.ts @@ -46,7 +46,7 @@ export class InstallExpoGoApplication extends Command { const currentSdkVersion = await expoHelper.exponentSdk(true); const expoUrlInfo = jsonContent.sdkVersions[currentSdkVersion]; - if (item == "Android") { + if (item === "Android") { void vscode.window.showInformationMessage("Downloading Expo Go for Android."); logger.logStream( localize("DownloadAndroidExpoGo", "\nDownloading Expo Go for Android. \n"), @@ -70,7 +70,7 @@ export class InstallExpoGoApplication extends Command { } } - if (installItem == "Auto") { + if (installItem === "Auto") { try { await installAndroidApplication(this.project, fileName); } catch { @@ -86,8 +86,8 @@ export class InstallExpoGoApplication extends Command { ), ); } - } else if (item == "iOS") { - if (os.platform() != "darwin") { + } else if (item === "iOS") { + if (os.platform() !== "darwin") { logger.warning( localize( "NotDarwinPlatform", @@ -122,7 +122,7 @@ export class InstallExpoGoApplication extends Command { } } - if (installItem == "Auto" && os.platform() == "darwin") { + if (installItem === "Auto" && os.platform() === "darwin") { try { await installiOSApplication(this.project, tarFile); } catch { diff --git a/src/extension/commands/launchExpoWeb.ts b/src/extension/commands/launchExpoWeb.ts index 4e78737ef..cb86ecf85 100644 --- a/src/extension/commands/launchExpoWeb.ts +++ b/src/extension/commands/launchExpoWeb.ts @@ -20,7 +20,7 @@ export class launchExpoWeb extends Command { label = "Launch ExpoWeb"; error = ErrorHelper.getInternalError(InternalErrorCode.FailedToLaunchExpoWeb); - async baseFn(launchArgs: any): Promise { + async baseFn(): Promise { assert(this.project); const expoHelper = this.project.getExponentHelper(); logger.info(localize("CheckExpoEnvironment", "Checking Expo project environment.")); diff --git a/src/extension/commands/openEASProject.ts b/src/extension/commands/openEASProject.ts index 268115c21..aa5309097 100644 --- a/src/extension/commands/openEASProject.ts +++ b/src/extension/commands/openEASProject.ts @@ -42,14 +42,14 @@ export class OpenEASProject extends ReactNativeCommand { await expoHelper.getExpoEasProjectName().then(result => { name = result; }); - if (id == null || owner == null) { + if (id === null || owner === null) { const error = localize( "ExpoProjectNotLinkToEAS", "Your app not link to EAS project. Please run 'eas init' firstly to bind your app to EAS project.", ); void vscode.window.showErrorMessage(error); logger.error(error); - } else if (name != null) { + } else if (name !== null) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions const url = `https://expo.dev/accounts/${owner}/projects/${name}`; await vscode.env.openExternal(vscode.Uri.parse(url)); diff --git a/src/extension/commands/revertOpenModule.ts b/src/extension/commands/revertOpenModule.ts index f417acf2c..75acade5c 100644 --- a/src/extension/commands/revertOpenModule.ts +++ b/src/extension/commands/revertOpenModule.ts @@ -78,7 +78,7 @@ export class RevertOpenModule extends ReactNativeCommand { const mainFilePath = path.resolve(openModulePath, "open-main.js"); if (fs.existsSync(mainFilePath)) { try { - await fs.unlinkSync(mainFilePath); + fs.unlinkSync(mainFilePath); } catch { logger.error( localize("FailedToDeleteMainFile", "Failed to delete open-main.js file."), @@ -95,10 +95,10 @@ export class RevertOpenModule extends ReactNativeCommand { const packageFilePath = path.resolve(openModulePath, "package.json"); const packageJson = JSON.parse(fs.readFileSync(packageFilePath, "utf-8")); - if (packageJson.main == "open-main.js") { + if (packageJson.main === "open-main.js") { try { delete packageJson.main; - await fs.writeFileSync( + fs.writeFileSync( packageFilePath, JSON.stringify(>packageJson), ); diff --git a/src/extension/commands/runEasBuild.ts b/src/extension/commands/runEasBuild.ts index 7481462ea..818f110b3 100644 --- a/src/extension/commands/runEasBuild.ts +++ b/src/extension/commands/runEasBuild.ts @@ -15,8 +15,8 @@ export class runEasBuild extends ReactNativeCommand { error = ErrorHelper.getInternalError(InternalErrorCode.FailedToRunEasBuild); async baseFn(): Promise { assert(this.project); - const packager = await this.project.getPackager(); - const projectRootPath = await packager.getProjectPath(); + const packager = this.project.getPackager(); + const projectRootPath = packager.getProjectPath(); if (!projectRootPath) { void vscode.window.showErrorMessage( diff --git a/src/extension/commands/stopPackager.ts b/src/extension/commands/stopPackager.ts index 096d885a5..af05442b2 100644 --- a/src/extension/commands/stopPackager.ts +++ b/src/extension/commands/stopPackager.ts @@ -17,11 +17,8 @@ export class StopPackager extends ReactNativeCommand<[AppLauncher]> { async onBeforeExecute(appLauncher: AppLauncher): Promise { await super.onBeforeExecute(appLauncher); - if (appLauncher instanceof AppLauncher) { - this.project = appLauncher; - } else { - this.project = await this.selectProject(); - } + this.project = + appLauncher instanceof AppLauncher ? appLauncher : await this.selectProject(); } // this function requires argument because we need it in extension 'deactivate' hook diff --git a/src/extension/commands/testDevEnvironment.ts b/src/extension/commands/testDevEnvironment.ts index a4eae2a31..77b85003b 100644 --- a/src/extension/commands/testDevEnvironment.ts +++ b/src/extension/commands/testDevEnvironment.ts @@ -64,13 +64,11 @@ export class TestDevEnvironment extends Command { [ValidationCategoryE.macOS]: (projectObserver && projectObserver.isRNMacosProject) || false, } as const; - if (project && projectObserver) { - await runChecks( - shouldCheck, - RNPackageVersionsToPackageVersion(projectObserver.rnPackageVersions), - ); - } else { - await runChecks(shouldCheck); - } + await (project && projectObserver + ? runChecks( + shouldCheck, + RNPackageVersionsToPackageVersion(projectObserver.rnPackageVersions), + ) + : runChecks(shouldCheck)); } } diff --git a/src/extension/commands/util/index.ts b/src/extension/commands/util/index.ts index 8d413ceba..f126e1de1 100644 --- a/src/extension/commands/util/index.ts +++ b/src/extension/commands/util/index.ts @@ -112,7 +112,10 @@ export async function selectProject(): Promise { return ProjectsStorage.projectsCache[selected]; } -export async function sendMessageToMetro(method: "devMenu" | "reload", project: AppLauncher) { +export async function sendMessageToMetro( + method: "devMenu" | "reload", + project: AppLauncher, +): Promise { const port = SettingsHelper.getPackagerPort(project.getWorkspaceFolderUri().fsPath); const ws = new WebSocket(`ws://localhost:${port}/message`); await new Promise(resolve => { diff --git a/src/extension/debuggingConfiguration/multiStepInput.ts b/src/extension/debuggingConfiguration/multiStepInput.ts index d4f48a48a..418a0f267 100644 --- a/src/extension/debuggingConfiguration/multiStepInput.ts +++ b/src/extension/debuggingConfiguration/multiStepInput.ts @@ -112,11 +112,7 @@ export class MultiStepInput implements IMultiStepInput { input.items = items; input.matchOnDescription = matchOnDescription || false; input.matchOnDetail = matchOnDetail || false; - if (activeItem) { - input.activeItems = [activeItem]; - } else { - input.activeItems = []; - } + input.activeItems = activeItem ? [activeItem] : []; input.buttons = [ ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), ...(buttons || []), @@ -242,6 +238,7 @@ export class MultiStepInput implements IMultiStepInput { this.current.busy = true; } try { + // eslint-disable-next-line no-await-in-loop -- each step depends on the previous step's result step = await step(this, state); } catch (err) { if (err === InputFlowAction.back) { diff --git a/src/extension/exponent/exponentHelper.ts b/src/extension/exponent/exponentHelper.ts index 024bbc314..75dfeeb4d 100644 --- a/src/extension/exponent/exponentHelper.ts +++ b/src/extension/exponent/exponentHelper.ts @@ -312,9 +312,10 @@ export class ExponentHelper { this.fs.exists(this.pathToFileInWorkspace(DEFAULT_IOS_INDEX)), this.fs.exists(this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX)), ]); - return expo - ? this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX) - : ios + if (expo) { + return this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX); + } + return ios ? this.pathToFileInWorkspace(DEFAULT_IOS_INDEX) : this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX); } diff --git a/src/extension/exponent/exponentPlatform.ts b/src/extension/exponent/exponentPlatform.ts index b6297b4e4..b85c80176 100644 --- a/src/extension/exponent/exponentPlatform.ts +++ b/src/extension/exponent/exponentPlatform.ts @@ -57,11 +57,9 @@ export class ExponentPlatform extends GeneralPlatform { // https://github.com/expo/expo-cli/blob/1d515d21200841e181518358fd9dc4c7b24c7cd6/packages/xdl/src/Project.ts#L2226-L2370 // we added this to be sure that our Expo launching logic doesn't have any negative side effects - if (this.runOptions.expoHostType === "tunnel") { - await this.prepareExpoTunnels(); - } else { - await XDL.stopAdbReverse(this.projectPath); - } + await (this.runOptions.expoHostType === "tunnel" + ? this.prepareExpoTunnels() + : XDL.stopAdbReverse(this.projectPath)); const isAdbReversed = this.runOptions.expoHostType !== "local" @@ -207,6 +205,6 @@ export class ExponentPlatform extends GeneralPlatform { } } -export function getQRCodeUrl() { +export function getQRCodeUrl(): string { return QRCodeUrl; } diff --git a/src/extension/generalMobilePlatform.ts b/src/extension/generalMobilePlatform.ts index c33f08671..ea4a311c2 100644 --- a/src/extension/generalMobilePlatform.ts +++ b/src/extension/generalMobilePlatform.ts @@ -82,15 +82,13 @@ export abstract class GeneralMobilePlatform extends GeneralPlatform { ), ); cleanupTargetModifications(); - } else { + } else if (this instanceof IOSPlatform || (await this.needToPassTargetToRunArgs())) { // For iOS we should pass exact target id, // because the “react-native run-ios” command does not check booted devices // and just launches the first device - if (this instanceof IOSPlatform || (await this.needToPassTargetToRunArgs())) { - this.addTargetToRunArgs(this.target); - } else { - cleanupTargetModifications(); - } + this.addTargetToRunArgs(this.target); + } else { + cleanupTargetModifications(); } } catch (error) { if ( diff --git a/src/extension/generalPlatform.ts b/src/extension/generalPlatform.ts index 5aa010d98..66164bae2 100644 --- a/src/extension/generalPlatform.ts +++ b/src/extension/generalPlatform.ts @@ -214,7 +214,7 @@ export class GeneralPlatform { if (envFile) { // .env variables never overwrite existing variables const argsFromEnvFile = this.readEnvFile(envFile); - if (argsFromEnvFile != null) { + if (argsFromEnvFile !== null && argsFromEnvFile !== undefined) { // eslint-disable-next-line no-restricted-syntax for (const key in argsFromEnvFile) { if (argsFromEnvFile.hasOwnProperty(key) && !modifyEnv.hasOwnProperty(key)) { @@ -247,7 +247,7 @@ export class GeneralPlatform { } buffer.split("\n").forEach((line: string) => { - const r = line.match(/^\s*([\w.\-]+)\s*=\s*(.*)?\s*$/); + const r = line.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/); if (r !== null) { const key = r[1]; let value = r[2] || ""; diff --git a/src/extension/ios/iOSTargetManager.ts b/src/extension/ios/iOSTargetManager.ts index 6408c9bf8..f047dd4da 100644 --- a/src/extension/ios/iOSTargetManager.ts +++ b/src/extension/ios/iOSTargetManager.ts @@ -198,7 +198,7 @@ export class IOSTargetManager extends MobileTargetManager { filter as ((el: IMobileTarget) => boolean) | undefined, )) as IDebuggableIOSTarget[]; // If we select only from devices, we should not select system - if (!targets.find(target => target.isVirtualTarget)) { + if (!targets.some(target => target.isVirtualTarget)) { return IOSTargetManager.ANY_SYSTEM; } const names: Set = new Set(targets.map(target => target.system)); diff --git a/src/extension/ios/plistBuddy.ts b/src/extension/ios/plistBuddy.ts index 172400af1..c92ded9b6 100644 --- a/src/extension/ios/plistBuddy.ts +++ b/src/extension/ios/plistBuddy.ts @@ -69,8 +69,9 @@ export class PlistBuddy { simulator: boolean = true, configuration: string = "Debug", productName?: string, - scheme?: string, + initialScheme?: string, ): Promise { + let scheme = initialScheme; const rnVersions = await ProjectVersionHelper.getReactNativeVersions(projectRoot); let productsFolder; if ( @@ -251,10 +252,11 @@ export class PlistBuddy { const nodeModulesRoot: string = AppLauncher.getNodeModulesRootByProjectPath(projectRoot); + const applePlatform = semver.gte(rnVersion, PlistBuddy.RN_VERSION_CLI_CONFIG_APPLE) + ? "cli-config-apple" + : "cli-platform-apple"; const iOSCliPlatform = semver.gte(rnVersion, PlistBuddy.RN_VERSION_CLI_PLATFORM_APPLE) - ? semver.gte(rnVersion, PlistBuddy.RN_VERSION_CLI_CONFIG_APPLE) - ? "cli-config-apple" - : "cli-platform-apple" + ? applePlatform : "cli-platform-ios"; const iOSCliFolderName = semver.gte(rnVersion, PlistBuddy.NEW_RN_IOS_CLI_LOCATION_VERSION) || @@ -270,7 +272,7 @@ export class PlistBuddy { fs.existsSync(pnpmProjectPath) && SettingsHelper.getPackageManager() === "pnpm"; if (isPnpmProject) { const modules = fs.readdirSync(pnpmProjectPath); - const regex = new RegExp(`\@react-native-community\\+${iOSCliFolderName}@`); + const regex = new RegExp(`@react-native-community\\+${iOSCliFolderName}@`); const communityModule = modules.find(module => regex.test(module)); if (communityModule) { findXcodeBase = path.join( diff --git a/src/extension/log/ConsoleLogger.ts b/src/extension/log/ConsoleLogger.ts index 9ffc01e9b..7455f924e 100644 --- a/src/extension/log/ConsoleLogger.ts +++ b/src/extension/log/ConsoleLogger.ts @@ -4,7 +4,8 @@ import { ILogger, LogLevel, LogHelper } from "./LogHelper"; export class ConsoleLogger implements ILogger { - public log(message: string, level: LogLevel): void { + public log(initialMessage: string, level: LogLevel): void { + let message = initialMessage; if (LogHelper.LOG_LEVEL === LogLevel.None) { return; } diff --git a/src/extension/log/OutputChannelLogger.ts b/src/extension/log/OutputChannelLogger.ts index dc0a1efde..5ed104bd5 100644 --- a/src/extension/log/OutputChannelLogger.ts +++ b/src/extension/log/OutputChannelLogger.ts @@ -78,7 +78,8 @@ export class OutputChannelLogger implements ILogger { } } - public log(message: string, level: LogLevel): void { + public log(initialMessage: string, level: LogLevel): void { + let message = initialMessage; if (LogHelper.LOG_LEVEL === LogLevel.None) { return; } @@ -96,7 +97,8 @@ export class OutputChannelLogger implements ILogger { } } - public logWithCustomTag(tag: string, message: string, level: LogLevel): void { + public logWithCustomTag(tag: string, initialMessage: string, level: LogLevel): void { + let message = initialMessage; if (LogHelper.LOG_LEVEL === LogLevel.None) { return; } diff --git a/src/extension/networkInspector/views/inspectorConsoleView.ts b/src/extension/networkInspector/views/inspectorConsoleView.ts index 5f4aa23ed..da41f0d2e 100644 --- a/src/extension/networkInspector/views/inspectorConsoleView.ts +++ b/src/extension/networkInspector/views/inspectorConsoleView.ts @@ -74,11 +74,10 @@ export class InspectorConsoleView extends InspectorView { } private setupConsoleLogsColor(systemColorTheme: SystemColorTheme): void { - if (systemColorTheme === SystemColorTheme.Light) { - this.consoleLogsColor = this.consoleLogsColors.Blue; - } else { - this.consoleLogsColor = this.consoleLogsColors.Orange; - } + this.consoleLogsColor = + systemColorTheme === SystemColorTheme.Light + ? this.consoleLogsColors.Blue + : this.consoleLogsColors.Orange; } private handleRequest(data: Request): void { @@ -169,13 +168,13 @@ export class InspectorConsoleView extends InspectorView { const response: Response = partialResponseEntry.initialResponse; const allChunks: string[] = - response.data != null + response.data !== null && response.data !== undefined ? [ response.data, ...Object.entries(partialResponseEntry.followupChunks) // It's important to parseInt here or it sorts lexicographically .sort((a, b) => parseInt(a[0], 10) - parseInt(b[0], 10)) - .map(([_k, v]: [string, string]) => v), + .map(([, v]: [string, string]) => v), ] : []; const data = combineBase64Chunks(allChunks); @@ -236,10 +235,11 @@ export class InspectorConsoleView extends InspectorView { } private prepareHeadersViewObject(headers: Header[]): Record { - return headers.reduce((headersViewObject, header) => { + const headersViewObject: Record = {}; + for (const header of headers) { headersViewObject[header.key] = header.value; - return headersViewObject; - }, {} as Record); + } + return headersViewObject; } private printNetworkRequestData(networkRequestData: ConsoleNetworkRequestDataView): void { diff --git a/src/extension/packagerStatusIndicator.ts b/src/extension/packagerStatusIndicator.ts index fad07cae1..74cb2b8d7 100644 --- a/src/extension/packagerStatusIndicator.ts +++ b/src/extension/packagerStatusIndicator.ts @@ -113,10 +113,11 @@ export class PackagerStatusIndicator implements Disposable { } private setupPackagerStatusIndicatorItems( - icon: string, + initialIcon: string, command?: string, tooltip: string = "", ): void { + let icon = initialIcon; this.updateDisplayVersion(); this.togglePackagerItem.command = command; this.togglePackagerItem.tooltip = this.getTooltip(tooltip); diff --git a/src/extension/reactNativeSessionManager.ts b/src/extension/reactNativeSessionManager.ts index 3bb81dbd2..9865ca89f 100644 --- a/src/extension/reactNativeSessionManager.ts +++ b/src/extension/reactNativeSessionManager.ts @@ -23,27 +23,25 @@ export class ReactNativeSessionManager ): vscode.ProviderResult { const rnSession = new RNSession(session); - let debugServer: Net.Server; - if (session.configuration.platform != "expoweb") { - debugServer = Net.createServer(socket => { - const rnDebugSession = - session.type === DEBUG_TYPES.REACT_NATIVE - ? new RNDebugSession(rnSession) - : new DirectDebugSession(rnSession); + const debugServer: Net.Server = + session.configuration.platform !== "expoweb" + ? Net.createServer(socket => { + const rnDebugSession = + session.type === DEBUG_TYPES.REACT_NATIVE + ? new RNDebugSession(rnSession) + : new DirectDebugSession(rnSession); - this.connections.set(session.id, socket); + this.connections.set(session.id, socket); - rnDebugSession.setRunAsServer(true); - rnDebugSession.start(socket, socket); - }); - } else { - debugServer = Net.createServer(socket => { - const webDebugSession = new WebDebugSession(rnSession); - webDebugSession.setRunAsServer(true); - this.connections.set(session.id, socket); - webDebugSession.start(socket, socket); - }); - } + rnDebugSession.setRunAsServer(true); + rnDebugSession.start(socket, socket); + }) + : Net.createServer(socket => { + const webDebugSession = new WebDebugSession(rnSession); + webDebugSession.setRunAsServer(true); + this.connections.set(session.id, socket); + webDebugSession.start(socket, socket); + }); return new Promise((resolve, reject) => { debugServer.once("error", reject); diff --git a/src/extension/rn-extension.ts b/src/extension/rn-extension.ts index 50f7a330b..86593f068 100644 --- a/src/extension/rn-extension.ts +++ b/src/extension/rn-extension.ts @@ -356,9 +356,9 @@ export async function onFolderAdded(folder: vscode.WorkspaceFolder): Promise val.original !== val.resolved, + ([, val]) => val.original !== val.resolved, )?.[0]; if (valUsesEnv) { diff --git a/src/extension/services/validationService/checks/visualStudio.ts b/src/extension/services/validationService/checks/visualStudio.ts index ed6521689..6d0f51510 100644 --- a/src/extension/services/validationService/checks/visualStudio.ts +++ b/src/extension/services/validationService/checks/visualStudio.ts @@ -15,12 +15,9 @@ const toLocale = nls.loadMessageBundle(); const label = "Compilers, build tools, SDKs and Visual Studio"; async function test(): Promise { - let vswherePath = ``; - if (process.env["ProgramFiles(x86)"]) { - vswherePath = `"${process.env["ProgramFiles(x86)"]}\\Microsoft Visual Studio\\Installer\\vswhere.exe"`; - } else { - vswherePath = `"C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe"`; - } + const vswherePath = process.env["ProgramFiles(x86)"] + ? `"${process.env["ProgramFiles(x86)"]}\\Microsoft Visual Studio\\Installer\\vswhere.exe"` + : `"C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe"`; const components = [ "Microsoft.Component.MSBuild", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", @@ -35,6 +32,7 @@ async function test(): Promise { const valid = (version: string) => semver.gtr(version, "16.5"); if (versions.some(valid)) { for (const comp of components) { + // eslint-disable-next-line no-await-in-loop -- returns early on the first missing component const pathToComponent = await executeCommand( `${vswherePath} -requires ${comp} -property productPath`, ); diff --git a/src/extension/services/validationService/util.ts b/src/extension/services/validationService/util.ts index 6030eb40e..01509e66e 100644 --- a/src/extension/services/validationService/util.ts +++ b/src/extension/services/validationService/util.ts @@ -47,11 +47,8 @@ export const basicCheck = async (arg: { return result; } - result.versionCompare = semver.gtr(version, arg.versionRange) - ? 1 - : semver.ltr(version, arg.versionRange) - ? -1 - : 0; + const lowerCompare = semver.ltr(version, arg.versionRange) ? -1 : 0; + result.versionCompare = semver.gtr(version, arg.versionRange) ? 1 : lowerCompare; return result; }; @@ -73,10 +70,12 @@ export const parseVersion = async ( // change typescript lib to es2019 ? export const fromEntries = ( entries: Iterable, -): Record => - [...entries].reduce((obj, [key, val]) => { +): Record => { + const obj = {} as Record; + for (const [key, val] of entries) { obj[key] = val; - return obj; - }, {} as Record); + } + return obj; +}; // export const flatten = (ary: any[]): unknown[] => diff --git a/src/extension/settingsHelper.ts b/src/extension/settingsHelper.ts index cc09cc56d..4aca8ce49 100644 --- a/src/extension/settingsHelper.ts +++ b/src/extension/settingsHelper.ts @@ -229,7 +229,7 @@ export class SettingsHelper { return []; } - public static getWorkspaceTelemetry(fsPath: string) { + public static getWorkspaceTelemetry(fsPath: string): any { if (!fsPath || !fs.existsSync(fsPath)) { return ""; } diff --git a/test/extension/commands/launchExpoWeb.test.ts b/test/extension/commands/launchExpoWeb.test.ts index 0af6d0eb0..401481fcd 100644 --- a/test/extension/commands/launchExpoWeb.test.ts +++ b/test/extension/commands/launchExpoWeb.test.ts @@ -83,7 +83,7 @@ suite("launchExpoWebCommand", function () { const command = launchExpoWeb.formInstance(); (command as any).project = project; - await command.baseFn({}); + await command.baseFn(); assert.strictEqual(isExpoManagedAppStub.calledWithExactly(true), true); assert.strictEqual(getRunOptionsStub.called, false); @@ -107,7 +107,7 @@ suite("launchExpoWebCommand", function () { const command = launchExpoWeb.formInstance(); (command as any).project = project; - await command.baseFn({}); + await command.baseFn(); assert.strictEqual(isExpoManagedAppStub.calledWithExactly(true), true); assert.strictEqual(