microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
960a6ec47eafad42cd52a36af455604d4650cfff

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/macos/macOSPlatform.ts

240lines · 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 * as semver from "semver";
5import * as path from "path";
6import { GeneralMobilePlatform, MobilePlatformDeps } from "../generalMobilePlatform";
7import { MacOSDebugModeManager } from "./macOSDebugModeManager";
8import { ImacOSRunOptions, PlatformType } from "../launchArgs";
9import { OutputVerifier, PatternToFailure } from "../../common/outputVerifier";
10import { TelemetryHelper } from "../../common/telemetryHelper";
11import { CommandExecutor } from "../../common/commandExecutor";
12import { InternalErrorCode } from "../../common/error/internalErrorCode";
13import { PlistBuddy, IOSBuildLocationData } from "../ios/plistBuddy";
14import { ChildProcess } from "../../common/node/childProcess";
15
16/**
17 * macOS specific platform implementation for debugging RN applications.
18 */
19export class MacOSPlatform extends GeneralMobilePlatform {
20 private static SUCCESS_PATTERNS = ["Launching app"];
21 private static FAILURE_PATTERNS: PatternToFailure[] = [
22 {
23 pattern: "Unrecognized command 'run-macos'",
24 errorCode: InternalErrorCode.ReactNativemacOSIsNotInstalled,
25 },
26 ];
27
28 public static DEFAULT_MACOS_PROJECT_RELATIVE_PATH = "macos";
29
30 private macosProjectRoot: string;
31 private plistBuddy: PlistBuddy;
32 private macOSDebugModeManager: MacOSDebugModeManager;
33
34 constructor(protected runOptions: ImacOSRunOptions, platformDeps: MobilePlatformDeps = {}) {
35 super(runOptions, platformDeps);
36
37 const macosProjectFolderPath = MacOSPlatform.getOptFromRunArgs(
38 this.runArguments,
39 "--project-path",
40 false,
41 );
42 this.macosProjectRoot = path.join(
43 this.projectPath,
44 macosProjectFolderPath || MacOSPlatform.DEFAULT_MACOS_PROJECT_RELATIVE_PATH,
45 );
46 this.plistBuddy = new PlistBuddy();
47
48 const schemeFromArgs = MacOSPlatform.getOptFromRunArgs(
49 this.runArguments,
50 "--scheme",
51 false,
52 );
53 this.macOSDebugModeManager = new MacOSDebugModeManager(
54 this.macosProjectRoot,
55 this.projectPath,
56 schemeFromArgs ? schemeFromArgs : this.runOptions.scheme,
57 );
58 }
59
60 public runApp(): Promise<void> {
61 let extProps: any = {
62 platform: {
63 value: PlatformType.macOS,
64 isPii: false,
65 },
66 };
67
68 if (this.runOptions.isDirect) {
69 extProps.isDirect = {
70 value: true,
71 isPii: false,
72 };
73 }
74
75 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
76 this.runOptions,
77 this.runOptions.reactNativeVersions,
78 extProps,
79 );
80
81 return TelemetryHelper.generate("MacOSPlatform.runApp", extProps, () => {
82 const env = GeneralMobilePlatform.getEnvArgument(
83 process.env,
84 this.runOptions.env,
85 this.runOptions.envFile,
86 );
87
88 if (
89 !semver.valid(
90 this.runOptions.reactNativeVersions.reactNativeVersion,
91 ) /*Custom RN implementations should support this flag*/ ||
92 semver.gte(
93 this.runOptions.reactNativeVersions.reactNativeVersion,
94 MacOSPlatform.NO_PACKAGER_VERSION,
95 )
96 ) {
97 this.runArguments.push("--no-packager");
98 }
99
100 const runmacOSSpawn = new CommandExecutor(
101 this.runOptions.nodeModulesRoot,
102 this.projectPath,
103 this.logger,
104 ).spawnReactCommand(`run-${this.platformName}`, this.runArguments, { env });
105 return new OutputVerifier(
106 () => Promise.resolve(MacOSPlatform.SUCCESS_PATTERNS),
107 () => Promise.resolve(MacOSPlatform.FAILURE_PATTERNS),
108 this.platformName,
109 ).process(runmacOSSpawn);
110 });
111 }
112
113 public prewarmBundleCache(): Promise<void> {
114 return this.packager.prewarmBundleCache(PlatformType.macOS);
115 }
116
117 public getRunArguments(): string[] {
118 let runArguments: string[] = [];
119
120 if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
121 runArguments.push(...this.runOptions.runArguments);
122 } else {
123 let target =
124 this.runOptions.target === MacOSPlatform.simulatorString
125 ? ""
126 : this.runOptions.target;
127 if (target) {
128 runArguments.push(`--${target}`);
129 }
130 }
131
132 return runArguments;
133 }
134
135 public enableJSDebuggingMode(): Promise<void> {
136 // Configure the app for debugging
137 // Wait until the configuration file exists, and check to see if debugging is enabled
138 return Promise.all<boolean | string>([
139 this.macOSDebugModeManager.getAppRemoteDebuggingSetting(
140 this.runOptions.configuration,
141 this.runOptions.productName,
142 ),
143 this.getApplicationName(),
144 ]).then(([debugModeEnabled, appName]) => {
145 if (debugModeEnabled) {
146 return Promise.resolve();
147 }
148
149 // Debugging must still be enabled
150 // We enable debugging by writing to a plist file that backs a NSUserDefaults object,
151 // but that file is written to by the app on occasion. To avoid races, we shut the app
152 // down before writing to the file.
153 return (
154 this.terminateMacOSapp(<string>appName)
155 // Write to the settings file while the app is not running to avoid races
156 .then(() =>
157 this.macOSDebugModeManager.setAppRemoteDebuggingSetting(
158 /*enable=*/ true,
159 this.runOptions.configuration,
160 this.runOptions.productName,
161 ),
162 )
163 .then(() => {
164 // Relaunch the app
165 return this.runApp();
166 })
167 );
168 });
169 }
170
171 public disableJSDebuggingMode(): Promise<void> {
172 return this.macOSDebugModeManager.setAppRemoteDebuggingSetting(
173 /*enable=*/ false,
174 this.runOptions.configuration,
175 this.runOptions.productName,
176 );
177 }
178
179 private getApplicationName(): Promise<string> {
180 return this.plistBuddy
181 .getExecutableAndConfigurationFolder(
182 this.macosProjectRoot,
183 this.projectPath,
184 PlatformType.macOS,
185 false,
186 this.runOptions.configuration,
187 this.runOptions.productName,
188 this.getSchemeFromDebuggingParameters(),
189 )
190 .then((iOSBuildLocationData: IOSBuildLocationData) => {
191 return iOSBuildLocationData.executable;
192 });
193 }
194
195 private getSchemeFromDebuggingParameters(): string | undefined {
196 let scheme = this.runOptions.scheme;
197 if (!scheme) {
198 const schemeFromArgs = MacOSPlatform.getOptFromRunArgs(
199 this.runArguments,
200 "--scheme",
201 false,
202 );
203 if (schemeFromArgs) {
204 scheme = schemeFromArgs;
205 }
206 }
207 return scheme;
208 }
209
210 private terminateMacOSapp(appName: string): Promise<void> {
211 let childProcess = new ChildProcess();
212 return (
213 childProcess
214 .execToString(`ps -ax | grep ${appName}`)
215 // An example of the output from the command above:
216 // 40943 ?? 4:13.97 node /Users/user/Documents/rn_for_mac_proj/node_modules/.bin/react-native start --port 8081
217 // 40959 ?? 0:10.36 /Users/user/.nvm/versions/node/v10.19.0/bin/node /Users/user/Documents/rn_for_mac_proj/node_modules/metro/node_modules/jest-worker/build/workers/processChild.js
218 // 41004 ?? 0:21.34 /Users/user/Library/Developer/Xcode/DerivedData/rn_for_mac_proj-ghuavabiztosiqfqkrityjoxqfmv/Build/Products/Debug/rn_for_mac_proj.app/Contents/MacOS/rn_for_mac_proj
219 // 75514 ttys007 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn rn_for_mac_proj
220 .then(searchResults => {
221 if (searchResults) {
222 const processIdRgx = /(^\d*)\s\?\?/g;
223 // We are looking for a process whose path contains the "appName.app" part
224 const processData = searchResults
225 .split("\n")
226 .find(str => str.includes(appName));
227
228 if (processData) {
229 const match = processIdRgx.exec(processData.trim());
230 if (match && match[1]) {
231 // eslint-disable-next-line @typescript-eslint/no-empty-function
232 return childProcess.execToString(`kill ${match[1]}`).then(() => {});
233 }
234 }
235 }
236 return void 0;
237 })
238 );
239 }
240}
241