microsoft/vscode-react-native
Publicmirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable
src/debugger/launcher.ts
99lines · 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 | |
| 4 | import * as fs from "fs"; |
| 5 | import * as path from "path"; |
| 6 | import * as Q from "q"; |
| 7 | import {MultipleLifetimesAppWorker} from "./appWorker"; |
| 8 | import {Log} from "../common/log/log"; |
| 9 | import {ErrorHelper} from "../common/error/errorHelper"; |
| 10 | import {InternalErrorCode} from "../common/error/internalErrorCode"; |
| 11 | import {ScriptImporter} from "./scriptImporter"; |
| 12 | import {PlatformResolver} from "./platformResolver"; |
| 13 | import {TelemetryHelper} from "../common/telemetryHelper"; |
| 14 | import {IRunOptions} from "../common/launchArgs"; |
| 15 | import * as em from "../common/extensionMessaging"; |
| 16 | import {EntryPointHandler} from "../common/entryPointHandler"; |
| 17 | |
| 18 | export class Launcher { |
| 19 | private projectRootPath: string; |
| 20 | private extensionMessageSender = new em.ExtensionMessageSender(); |
| 21 | |
| 22 | constructor(projectRootPath: string) { |
| 23 | this.projectRootPath = projectRootPath; |
| 24 | } |
| 25 | |
| 26 | public launch(): void { |
| 27 | // Enable telemetry |
| 28 | new EntryPointHandler(true).runApp("react-native-debug-process", () => this.getAppVersion(), |
| 29 | ErrorHelper.getInternalError(InternalErrorCode.DebuggingFailed), () => { |
| 30 | return TelemetryHelper.generate("launch", (generator) => { |
| 31 | const resolver = new PlatformResolver(); |
| 32 | return this.parseRunOptions().then(runOptions => { |
| 33 | const mobilePlatform = resolver.resolveMobilePlatform(runOptions.platform); |
| 34 | if (!mobilePlatform) { |
| 35 | throw new RangeError("The target platform could not be read. Did you forget to add it to the launch.json configuration arguments?"); |
| 36 | } else { |
| 37 | const sourcesStoragePath = path.join(this.projectRootPath, ".vscode", ".react"); |
| 38 | let extensionMessageSender = new em.ExtensionMessageSender(); |
| 39 | return Q({}) |
| 40 | .then(() => { |
| 41 | generator.step("startPackager"); |
| 42 | return extensionMessageSender.sendMessage(em.ExtensionMessage.START_PACKAGER); |
| 43 | }) |
| 44 | .then(() => { |
| 45 | let scriptImporter = new ScriptImporter(runOptions.packagerPort, sourcesStoragePath); |
| 46 | return scriptImporter.downloadDebuggerWorker(sourcesStoragePath).then(() => { |
| 47 | Log.logMessage("Downloaded debuggerWorker.js (Logic to run the React Native app) from the Packager."); |
| 48 | }); |
| 49 | }) |
| 50 | // We've seen that if we don't prewarm the bundle cache, the app fails on the first attempt to connect to the debugger logic |
| 51 | // and the user needs to Reload JS manually. We prewarm it to prevent that issue |
| 52 | .then(() => { |
| 53 | generator.step("prewarmBundleCache"); |
| 54 | return extensionMessageSender.sendMessage(em.ExtensionMessage.PREWARM_BUNDLE_CACHE, [runOptions.platform]); |
| 55 | }) |
| 56 | .then(() => { |
| 57 | generator.step("mobilePlatform.runApp"); |
| 58 | return mobilePlatform.runApp(runOptions); |
| 59 | }) |
| 60 | .then(() => { |
| 61 | generator.step("Starting App Worker"); |
| 62 | return new MultipleLifetimesAppWorker(runOptions.packagerPort, sourcesStoragePath, runOptions.debugAdapterPort).start(); |
| 63 | }) // Start the app worker |
| 64 | .then(() => { |
| 65 | generator.step("mobilePlatform.enableJSDebuggingMode"); |
| 66 | return mobilePlatform.enableJSDebuggingMode(runOptions); |
| 67 | }).then(() => |
| 68 | Log.logMessage("Debugging session started successfully.")); |
| 69 | } |
| 70 | }); |
| 71 | }); |
| 72 | }); |
| 73 | } |
| 74 | |
| 75 | private getAppVersion() { |
| 76 | return JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf-8")).version; |
| 77 | } |
| 78 | |
| 79 | private getPackagerPort(): Q.Promise<number> { |
| 80 | return this.extensionMessageSender.sendMessage(em.ExtensionMessage.GET_PACKAGER_PORT); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Parses the launch arguments set in the launch configuration. |
| 85 | */ |
| 86 | private parseRunOptions(): Q.Promise<IRunOptions> { |
| 87 | // We expect our debugAdapter to pass in arguments as [platform, debugAdapterPort, target?, logCatArguments?]; |
| 88 | return this.getPackagerPort().then(packagerPort => { |
| 89 | return { |
| 90 | projectRoot: this.projectRootPath, |
| 91 | platform: process.argv[2].toLowerCase(), |
| 92 | debugAdapterPort: parseInt(process.argv[3], 10) || 9090, |
| 93 | target: process.argv[4], |
| 94 | packagerPort: packagerPort, |
| 95 | logCatArguments: process.argv[5], |
| 96 | }; |
| 97 | }); |
| 98 | } |
| 99 | } |