microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5759f79cea6d220d75c4589a028aec52100953d6

Branches

Tags

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

Clone

HTTPS

Download ZIP

test/smoke/package/src/helpers/setupEnvironmentHelper.ts

305lines · 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 path from "path";
5import * as utilities from "./utilities";
6import * as fs from "fs";
7import * as rimraf from "rimraf";
8import * as cp from "child_process";
9import * as semver from "semver";
10import * as os from "os";
11import { IosSimulatorHelper } from "./iosSimulatorHelper";
12import { sleep } from "./utilities";
13import { AndroidEmulatorHelper } from "./androidEmulatorHelper";
14const XDL = require("@expo/xdl");
15
16
17export class SetupEnvironmentHelper {
18
19 public static expoPackageName = "host.exp.exponent";
20 public static expoBundleId = "host.exp.Exponent";
21 public static iOSExpoAppsCacheDir = `${os.homedir()}/.expo/ios-simulator-app-cache`;
22
23 public static prepareReactNativeApplication(workspaceFilePath: string, resourcesPath: string, workspacePath: string, appName: string, customEntryPointFolder: string, version?: string) {
24 let command = `react-native init ${appName}`;
25 if (version) {
26 command += ` --version ${version}`;
27 }
28 console.log(`*** Creating RN app via '${command}' in ${workspacePath}...`);
29 cp.execSync(command, { cwd: resourcesPath, stdio: "inherit" });
30
31 const customEntryPointFile = path.join(resourcesPath, customEntryPointFolder, "App.js");
32 const launchConfigFile = path.join(resourcesPath, "launch.json");
33 const vsCodeConfigPath = path.join(workspacePath, ".vscode");
34
35 console.log(`*** Copying ${customEntryPointFile} into ${workspaceFilePath}...`);
36 fs.writeFileSync(workspaceFilePath, fs.readFileSync(customEntryPointFile));
37
38 if (!fs.existsSync(vsCodeConfigPath)) {
39 console.log(`*** Creating ${vsCodeConfigPath}...`);
40 fs.mkdirSync(vsCodeConfigPath);
41 }
42
43 console.log(`*** Copying ${launchConfigFile} into ${vsCodeConfigPath}...`);
44 fs.writeFileSync(path.join(vsCodeConfigPath, "launch.json"), fs.readFileSync(launchConfigFile));
45
46 SetupEnvironmentHelper.patchMetroConfig(workspacePath);
47 }
48
49 public static prepareHermesReactNativeApplication(workspaceFilePath: string, resourcesPath: string, workspacePath: string, appName: string, customEntryPointFolder: string, version?: string) {
50 const commandClean = path.join(workspacePath, "android", "gradlew") + " clean";
51
52 console.log(`*** Executing ${commandClean} ...`);
53 cp.execSync(commandClean, { cwd: path.join(workspacePath, "android"), stdio: "inherit" });
54
55 const customEntryPointFile = path.join(resourcesPath, customEntryPointFolder, "App.js");
56 const testButtonPath = path.join(resourcesPath, customEntryPointFolder, "AppTestButton.js");
57
58 console.log(`*** Copying ${customEntryPointFile} into ${workspaceFilePath}...`);
59 fs.writeFileSync(workspaceFilePath, fs.readFileSync(customEntryPointFile));
60
61 SetupEnvironmentHelper.copyGradleFilesToHermesApp(workspacePath, resourcesPath, customEntryPointFolder);
62
63 console.log(`*** Copying ${testButtonPath} into ${workspacePath}`);
64 fs.copyFileSync(testButtonPath, path.join(workspacePath, "AppTestButton.js"));
65 }
66
67 public static prepareExpoApplication(workspaceFilePath: string, resourcesPath: string, workspacePath: string, appName: string, expoSdkMajorVersion?: string) {
68 const useSpecificSdk = expoSdkMajorVersion ? `@sdk-${expoSdkMajorVersion}` : "";
69 const command = `echo -ne '\\n' | expo init -t tabs${useSpecificSdk} --name ${appName} ${appName}`;
70 console.log(`*** Creating Expo app via '${command}' in ${workspacePath}...`);
71 cp.execSync(command, { cwd: resourcesPath, stdio: "inherit" });
72
73 const customEntryPointFile = path.join(resourcesPath, "ExpoSample", "App.tsx");
74 const launchConfigFile = path.join(resourcesPath, "launch.json");
75 const vsCodeConfigPath = path.join(workspacePath, ".vscode");
76
77 console.log(`*** Copying ${customEntryPointFile} into ${workspaceFilePath}...`);
78 fs.writeFileSync(workspaceFilePath, fs.readFileSync(customEntryPointFile));
79
80 if (!fs.existsSync(vsCodeConfigPath)) {
81 console.log(`*** Creating ${vsCodeConfigPath}...`);
82 fs.mkdirSync(vsCodeConfigPath);
83 }
84
85 console.log(`*** Copying ${launchConfigFile} into ${vsCodeConfigPath}...`);
86 fs.writeFileSync(path.join(vsCodeConfigPath, "launch.json"), fs.readFileSync(launchConfigFile));
87
88 SetupEnvironmentHelper.patchMetroConfig(workspacePath);
89 }
90
91 public static addExpoDependencyToRNProject(workspacePath: string, version?: string) {
92 let npmCmd = "npm";
93 if (process.platform === "win32") {
94 npmCmd = "npm.cmd";
95 }
96
97 let expoPackage: string = version ? `expo@${version}` : "expo";
98 const command = `${npmCmd} install ${expoPackage} --save-dev`;
99
100 console.log(`*** Adding expo dependency to ${workspacePath} via '${command}' command...`);
101 cp.execSync(command, { cwd: workspacePath, stdio: "inherit" });
102 }
103
104 public static cleanUp(testVSCodeDirectory: string, userDataDir: string, testLogsDirectory: string, workspacePaths: string[], iOSExpoAppsCacheDirectory: string) {
105 console.log("\n*** Clean up...");
106 if (fs.existsSync(testVSCodeDirectory)) {
107 console.log(`*** Deleting test VS Code directory: ${testVSCodeDirectory}`);
108 rimraf.sync(testVSCodeDirectory);
109 }
110 if (fs.existsSync(userDataDir)) {
111 console.log(`*** Deleting VS Code temporary user data dir: ${userDataDir}`);
112 rimraf.sync(userDataDir);
113 }
114 if (fs.existsSync(testLogsDirectory)) {
115 console.log(`*** Deleting test logs directory: ${testLogsDirectory}`);
116 rimraf.sync(testLogsDirectory);
117 }
118 workspacePaths.forEach(testAppFolder => {
119 if (fs.existsSync(testAppFolder)) {
120 console.log(`*** Deleting test application: ${testAppFolder}`);
121 rimraf.sync(testAppFolder);
122 }
123 });
124 if (fs.existsSync(iOSExpoAppsCacheDirectory)) {
125 console.log(`*** Deleting iOS expo app cache directory: ${iOSExpoAppsCacheDirectory}`);
126 rimraf.sync(iOSExpoAppsCacheDirectory);
127 }
128 }
129
130 public static async getLatestSupportedRNVersionForExpo(expoSdkMajorVersion?: string): Promise<any> {
131 const printSpecifiedMajorVersion = expoSdkMajorVersion ? `sdk-${expoSdkMajorVersion}` : "";
132 const printIsLatest = printSpecifiedMajorVersion ? "" : "latest ";
133 console.log(`*** Getting latest React Native version supported by ${printIsLatest}Expo ${printSpecifiedMajorVersion}...`);
134 return new Promise((resolve, reject) => {
135 utilities.getContents("https://exp.host/--/api/v2/versions", null, null, function (error, versionsContent) {
136 if (error) {
137 reject(error);
138 }
139 try {
140 const content = JSON.parse(versionsContent);
141 if (content.sdkVersions) {
142 let usesSdkVersion: string | undefined;
143 if (expoSdkMajorVersion) {
144 usesSdkVersion = Object.keys(content.sdkVersions).find((version) => semver.major(version) === parseInt(expoSdkMajorVersion));
145 if (!usesSdkVersion) {
146 console.log(`*** Сould not find the version of Expo sdk matching the specified version - ${printSpecifiedMajorVersion}`);
147 }
148 }
149 if (!usesSdkVersion) {
150 usesSdkVersion = Object.keys(content.sdkVersions).sort((ver1, ver2) => {
151 if (semver.lt(ver1, ver2)) {
152 return 1;
153 } else if (semver.gt(ver1, ver2)) {
154 return -1;
155 }
156 return 0;
157 })[0];
158 }
159 if (content.sdkVersions[usesSdkVersion]) {
160 if (content.sdkVersions[usesSdkVersion].facebookReactNativeVersion) {
161 console.log(`*** Latest React Native version supported by Expo ${printSpecifiedMajorVersion}: ${content.sdkVersions[usesSdkVersion].facebookReactNativeVersion}`);
162 resolve(content.sdkVersions[usesSdkVersion].facebookReactNativeVersion as string);
163 }
164 }
165 }
166 reject("Received object is incorrect");
167 } catch (error) {
168 reject(error);
169 }
170 });
171 });
172 }
173
174 // Installs Expo app on Android device using XDL function
175 public static async installExpoAppOnAndroid() {
176 console.log(`*** Installing Expo app on Android emulator using Expo XDL function`);
177 await XDL.Android.installExpoAsync({
178 device: {
179 name: AndroidEmulatorHelper.getOnlineDevices()[0].id,
180 type: "emulator",
181 isBooted: true,
182 isAuthorized: true,
183 }
184 });
185 AndroidEmulatorHelper.enableDrawPermitForApp(this.expoPackageName);
186 }
187
188 // Installs Expo app on iOS device using XDL function
189 public static async installExpoAppOnIos() {
190 console.log(`*** Installing Expo app on iOS simulator using Expo XDL function`);
191 await XDL.Simulator.installExpoOnSimulatorAsync({
192 simulator: {
193 name: IosSimulatorHelper.getDevice() || "",
194 udid: IosSimulatorHelper.getDeviceUdid() || ""
195 }
196 });
197 }
198
199 // Fix for https://github.com/expo/expo-cli/issues/951
200 // TODO: Delete when bug will be fixed
201 public static patchExpoSettingsFile(expoAppPath: string) {
202 const settingsJsonPath = path.join(expoAppPath, ".expo", "settings.json");
203 if (fs.existsSync(settingsJsonPath)) {
204 console.log(`*** Patching ${settingsJsonPath}...`);
205 let content = JSON.parse(fs.readFileSync(settingsJsonPath).toString());
206 if (content.https === false) {
207 console.log(`*** Deleting https: ${content.https} line...`);
208 delete content.https;
209 content = JSON.stringify(content, null, 2);
210 fs.writeFileSync(settingsJsonPath, content);
211 }
212 }
213 }
214
215 public static setIosTargetToLaunchJson(workspacePath: string, configName: string, target?: string) {
216 let launchJsonPath = path.join(workspacePath, ".vscode", "launch.json");
217 if (target) {
218 console.log(`*** Implicitly adding target to "${configName}" config for ${launchJsonPath}`);
219 }
220 else {
221 console.log(`*** Implicitly remove target from "${configName}" config`);
222 }
223 let content = JSON.parse(fs.readFileSync(launchJsonPath).toString());
224 let found = false;
225 for (let i = 0; i < content.configurations.length; i++) {
226 if (content.configurations[i].name === configName) {
227 found = true;
228 if (!target) {
229 delete content.configurations[i].target;
230 }
231 else {
232 content.configurations[i].target = target;
233 }
234 }
235 }
236 if (!found) {
237 throw new Error("Couldn't find \"Debug iOS\" configuration");
238 }
239 fs.writeFileSync(launchJsonPath, JSON.stringify(content, undefined, 4)); // Adds indentations
240 }
241
242 public static async runIosSimulator() {
243 const device = <string>IosSimulatorHelper.getDevice();
244 await this.terminateIosSimulator();
245 // Wipe data on simulator
246 await IosSimulatorHelper.eraseSimulator(device);
247 console.log(`*** Executing iOS simulator with 'xcrun simctl boot "${device}"' command...`);
248 await IosSimulatorHelper.bootSimulator(device);
249 await sleep(15 * 1000);
250 }
251
252 public static async terminateIosSimulator() {
253 const device = <string>IosSimulatorHelper.getDevice();
254 await IosSimulatorHelper.shutdownSimulator(device);
255 }
256
257 public static installExpoXdlPackageToExtensionDir(extensionDir: any, packageVersion: string) {
258 let npmCmd = "npm";
259 if (process.platform === "win32") {
260 npmCmd = "npm.cmd";
261 }
262 const command = `${npmCmd} install @expo/xdl@${packageVersion} --no-save`;
263
264 console.log(`*** Adding @expo/xdl dependency to ${extensionDir} via '${command}' command...`);
265 cp.execSync(command, { cwd: extensionDir, stdio: "inherit" });
266 }
267
268 public static async patchMetroConfig(appPath: string) {
269 const metroConfigPath = path.join(appPath, "metro.config.js");
270 console.log(`*** Patching ${metroConfigPath}`);
271 const patchContent = `
272// Sometimes on Windows Metro fails to resolve files located at .vscode\.react directory and throws EPERM errors
273// To avoid it this directory is added to black list for resolving by Metro
274if (process.platform === "win32") {
275 module.exports.resolver = {
276 blacklistRE: /.*\.vscode\\\.react.*/
277 };
278}
279
280// Redirect Metro cache
281module.exports.cacheStores = [
282 new (require('metro-cache')).FileStore({
283 root: require('path').join(".cache", 'metro-cache'),
284 }),
285];
286
287// Redirect Haste Map cache
288module.exports.hasteMapCacheDirectory = ".cache";
289
290// Due to the fact that Metro bundler on MacOS has problems with scanning files and folders starting with a dot (hidden folders), for example './vscode',
291// the first time when the packager starts, it cannot find the './vscode/exponentIndex.js' file. So we add this folder to scanning manually.
292module.exports.watchFolders = ['.vscode'];`;
293 fs.appendFileSync(metroConfigPath, patchContent);
294 const contentAfterPatching = fs.readFileSync(metroConfigPath);
295 console.log(`*** Content of a metro.config.js after patching: ${contentAfterPatching}`);
296 }
297
298 private static copyGradleFilesToHermesApp(workspacePath: string, resourcesPath: string, customEntryPointFolder: string) {
299 const appGradleBuildFilePath = path.join(workspacePath, "android", "app", "build.gradle");
300 const resGradleBuildFilePath = path.join(resourcesPath, customEntryPointFolder, "build.gradle");
301
302 console.log(`*** Copying ${resGradleBuildFilePath} into ${appGradleBuildFilePath}...`);
303 fs.writeFileSync(appGradleBuildFilePath, fs.readFileSync(resGradleBuildFilePath));
304 }
305}
306