microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/v-peq/removeNode10TodoComments

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/commands/cleanRestartPackager.ts

152lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import assert = require("assert");
5import * as path from "path";
6import * as fs from "fs";
7import { ErrorHelper } from "../../common/error/errorHelper";
8import { InternalErrorCode } from "../../common/error/internalErrorCode";
9import { ProjectVersionHelper } from "../../common/projectVersionHelper";
10import { SettingsHelper } from "../settingsHelper";
11import { ChildProcess } from "../../common/node/childProcess";
12import { OutputChannelLogger } from "../log/OutputChannelLogger";
13import { HostPlatform, HostPlatformId } from "../../common/hostPlatform";
14import { ReactNativeCommand } from "./util/reactNativeCommand";
15
16const logger = OutputChannelLogger.getMainChannel();
17
18export class CleanRestartPackager extends ReactNativeCommand {
19 codeName = "cleanRestartPackager";
20 label = "Clean & Restart Packager";
21 error = ErrorHelper.getInternalError(InternalErrorCode.FailedToCleanRestartPackager);
22
23 async baseFn(): Promise<void> {
24 assert(this.project);
25 const nodeModulesRoot = this.project.getOrUpdateNodeModulesRoot();
26 await ProjectVersionHelper.getReactNativePackageVersionsFromNodeModules(nodeModulesRoot);
27
28 const projectPath = this.project.getPackager().getProjectPath();
29 const packagerPort = SettingsHelper.getPackagerPort(
30 this.project.getWorkspaceFolderUri().fsPath,
31 );
32
33 logger.info("Starting Metro Packager cleanup and restart...");
34
35 // Step 1: Kill Metro process on port 8081
36 await this.killMetroProcess(packagerPort);
37
38 // Step 2: Clean Metro cache
39 await this.cleanMetroCache(projectPath);
40
41 // Step 3: Clean Watchman cache (if available)
42 await this.cleanWatchmanCache();
43
44 // Step 4: Restart packager with reset cache
45 logger.info("Restarting Packager with clean cache...");
46 await this.project.getPackager().restart(packagerPort);
47
48 logger.info("Metro Packager cleanup and restart completed successfully.");
49 }
50
51 private async killMetroProcess(port: number): Promise<void> {
52 logger.info(`Step 1/3: Terminating Metro process on port ${port}...`);
53
54 try {
55 const platformId = HostPlatform.getPlatformId();
56 const childProcess = new ChildProcess();
57
58 if (platformId === HostPlatformId.WINDOWS) {
59 // Windows: Use netstat and taskkill
60 try {
61 const netstatResult = await childProcess.exec(
62 `netstat -ano | findstr :${port}`,
63 );
64 const outcome = await netstatResult.outcome;
65
66 if (outcome) {
67 // Extract PID from netstat output
68 const lines = outcome.split("\n");
69 for (const line of lines) {
70 const match = line.match(/\s+LISTENING\s+(\d+)/);
71 if (match && match[1]) {
72 const pid = match[1];
73 logger.info(`Found Metro process with PID: ${pid}`);
74 await childProcess.exec(`taskkill /PID ${pid} /F /T`);
75 logger.info(`Successfully terminated process ${pid}`);
76 }
77 }
78 }
79 } catch (error) {
80 logger.info(`No Metro process found on port ${port}`);
81 }
82 } else {
83 // macOS/Linux: Use lsof and kill
84 try {
85 const lsofResult = await childProcess.exec(`lsof -ti:${port}`);
86 const outcome = await lsofResult.outcome;
87
88 if (outcome && outcome.trim()) {
89 const pid = outcome.trim();
90 logger.info(`Found Metro process with PID: ${pid}`);
91 await childProcess.exec(`kill -9 ${pid}`);
92 logger.info(`Successfully terminated process ${pid}`);
93 }
94 } catch (error) {
95 logger.info(`No Metro process found on port ${port}`);
96 }
97 }
98 } catch (error) {
99 logger.warning(`Failed to kill Metro process: ${error}`);
100 }
101 }
102
103 private async cleanMetroCache(projectPath: string): Promise<void> {
104 logger.info("Step 2/3: Cleaning Metro cache...");
105
106 const metroCachePath = path.join(projectPath, "node_modules", ".cache", "metro");
107
108 try {
109 if (fs.existsSync(metroCachePath)) {
110 // Use recursive directory deletion
111 await this.deleteDirectory(metroCachePath);
112 logger.info(`Successfully cleaned Metro cache at: ${metroCachePath}`);
113 } else {
114 logger.info("Metro cache directory not found, skipping...");
115 }
116 } catch (error) {
117 logger.warning(`Failed to clean Metro cache: ${error}`);
118 }
119 }
120
121 private async cleanWatchmanCache(): Promise<void> {
122 logger.info("Step 3/3: Cleaning Watchman cache...");
123
124 try {
125 const childProcess = new ChildProcess();
126 const watchmanResult = await childProcess.exec("watchman watch-del-all");
127 await watchmanResult.outcome;
128 logger.info("Successfully cleaned Watchman cache");
129 } catch (error) {
130 logger.info("Watchman not available or failed to clean cache, continuing...");
131 }
132 }
133
134 private async deleteDirectory(dirPath: string): Promise<void> {
135 if (fs.existsSync(dirPath)) {
136 const files = fs.readdirSync(dirPath);
137
138 for (const file of files) {
139 const filePath = path.join(dirPath, file);
140 const stat = fs.statSync(filePath);
141
142 if (stat.isDirectory()) {
143 await this.deleteDirectory(filePath);
144 } else {
145 fs.unlinkSync(filePath);
146 }
147 }
148
149 fs.rmdirSync(dirPath);
150 }
151 }
152}
153