microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ffdf159274093d12e32a09aafae6d0de3099bb4a

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/macos/macOSPlatform.ts

233lines · 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 = {
62 platform: {
63 value: PlatformType.macOS,
64 isPii: false,
65 },
66 };
67
68 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
69 this.runOptions,
70 this.runOptions.reactNativeVersions,
71 extProps,
72 );
73
74 return TelemetryHelper.generate("MacOSPlatform.runApp", extProps, () => {
75 const env = GeneralMobilePlatform.getEnvArgument(
76 process.env,
77 this.runOptions.env,
78 this.runOptions.envFile,
79 );
80
81 if (
82 !semver.valid(
83 this.runOptions.reactNativeVersions.reactNativeVersion,
84 ) /*Custom RN implementations should support this flag*/ ||
85 semver.gte(
86 this.runOptions.reactNativeVersions.reactNativeVersion,
87 MacOSPlatform.NO_PACKAGER_VERSION,
88 )
89 ) {
90 this.runArguments.push("--no-packager");
91 }
92
93 const runmacOSSpawn = new CommandExecutor(
94 this.runOptions.nodeModulesRoot,
95 this.projectPath,
96 this.logger,
97 ).spawnReactCommand(`run-${this.platformName}`, this.runArguments, { env });
98 return new OutputVerifier(
99 () => Promise.resolve(MacOSPlatform.SUCCESS_PATTERNS),
100 () => Promise.resolve(MacOSPlatform.FAILURE_PATTERNS),
101 this.platformName,
102 ).process(runmacOSSpawn);
103 });
104 }
105
106 public prewarmBundleCache(): Promise<void> {
107 return this.packager.prewarmBundleCache(PlatformType.macOS);
108 }
109
110 public getRunArguments(): string[] {
111 let runArguments: string[] = [];
112
113 if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
114 runArguments.push(...this.runOptions.runArguments);
115 } else {
116 let target =
117 this.runOptions.target === MacOSPlatform.simulatorString
118 ? ""
119 : this.runOptions.target;
120 if (target) {
121 runArguments.push(`--${target}`);
122 }
123 }
124
125 return runArguments;
126 }
127
128 public enableJSDebuggingMode(): Promise<void> {
129 // Configure the app for debugging
130 // Wait until the configuration file exists, and check to see if debugging is enabled
131 return Promise.all<boolean | string>([
132 this.macOSDebugModeManager.getAppRemoteDebuggingSetting(
133 this.runOptions.configuration,
134 this.runOptions.productName,
135 ),
136 this.getApplicationName(),
137 ]).then(([debugModeEnabled, appName]) => {
138 if (debugModeEnabled) {
139 return Promise.resolve();
140 }
141
142 // Debugging must still be enabled
143 // We enable debugging by writing to a plist file that backs a NSUserDefaults object,
144 // but that file is written to by the app on occasion. To avoid races, we shut the app
145 // down before writing to the file.
146 return (
147 this.terminateMacOSapp(<string>appName)
148 // Write to the settings file while the app is not running to avoid races
149 .then(() =>
150 this.macOSDebugModeManager.setAppRemoteDebuggingSetting(
151 /*enable=*/ true,
152 this.runOptions.configuration,
153 this.runOptions.productName,
154 ),
155 )
156 .then(() => {
157 // Relaunch the app
158 return this.runApp();
159 })
160 );
161 });
162 }
163
164 public disableJSDebuggingMode(): Promise<void> {
165 return this.macOSDebugModeManager.setAppRemoteDebuggingSetting(
166 /*enable=*/ false,
167 this.runOptions.configuration,
168 this.runOptions.productName,
169 );
170 }
171
172 private getApplicationName(): Promise<string> {
173 return this.plistBuddy
174 .getExecutableAndConfigurationFolder(
175 this.macosProjectRoot,
176 this.projectPath,
177 PlatformType.macOS,
178 false,
179 this.runOptions.configuration,
180 this.runOptions.productName,
181 this.getSchemeFromDebuggingParameters(),
182 )
183 .then((iOSBuildLocationData: IOSBuildLocationData) => {
184 return iOSBuildLocationData.executable;
185 });
186 }
187
188 private getSchemeFromDebuggingParameters(): string | undefined {
189 let scheme = this.runOptions.scheme;
190 if (!scheme) {
191 const schemeFromArgs = MacOSPlatform.getOptFromRunArgs(
192 this.runArguments,
193 "--scheme",
194 false,
195 );
196 if (schemeFromArgs) {
197 scheme = schemeFromArgs;
198 }
199 }
200 return scheme;
201 }
202
203 private terminateMacOSapp(appName: string): Promise<void> {
204 let childProcess = new ChildProcess();
205 return (
206 childProcess
207 .execToString(`ps -ax | grep ${appName}`)
208 // An example of the output from the command above:
209 // 40943 ?? 4:13.97 node /Users/user/Documents/rn_for_mac_proj/node_modules/.bin/react-native start --port 8081
210 // 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
211 // 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
212 // 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
213 .then(searchResults => {
214 if (searchResults) {
215 const processIdRgx = /(^\d*)\s\?\?/g;
216 // We are looking for a process whose path contains the "appName.app" part
217 const processData = searchResults
218 .split("\n")
219 .find(str => str.includes(appName));
220
221 if (processData) {
222 const match = processIdRgx.exec(processData.trim());
223 if (match && match[1]) {
224 // eslint-disable-next-line @typescript-eslint/no-empty-function
225 return childProcess.execToString(`kill ${match[1]}`).then(() => {});
226 }
227 }
228 }
229 return void 0;
230 })
231 );
232 }
233}
234