microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
transitive-dependency-serialize-javascript

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commands/cleanRestartPackager.ts

152lines · modeblame

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