microsoft/vscode-react-native
Publicmirrored from https://github.com/microsoft/vscode-react-nativeAvailable
src/common/packager.ts
153lines · modeblame
a31b007cunknown10 years ago | 1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | // Licensed under the MIT license. See LICENSE file in the project root for details. | |
| 3 | | |
c9b4fa6cMeena Kunnathur Balakrishnan10 years ago | 4 | import {ChildProcess} from "child_process"; |
b0061ac6Meena Kunnathur Balakrishnan10 years ago | 5 | import {CommandExecutor} from "./commandExecutor"; |
| 6 | import {Log, LogLevel} from "./log"; | |
| 7 | import {Node} from "./node/node"; | |
bef522ffMeena Kunnathur Balakrishnan10 years ago | 8 | import {OutputChannel} from "vscode"; |
35390151Meena Kunnathur Balakrishnan10 years ago | 9 | import {Package} from "./node/package"; |
b0061ac6Meena Kunnathur Balakrishnan10 years ago | 10 | import {PromiseUtil} from "./node/promise"; |
| 11 | import {Request} from "./node/request"; | |
24aab397Meena Kunnathur Balakrishnan10 years ago | 12 | |
3fb37ad5unknown10 years ago | 13 | import * as Q from "q"; |
4677921cdigeff10 years ago | 14 | import * as path from "path"; |
3fb37ad5unknown10 years ago | 15 | |
| 16 | export class Packager { | |
418d8ac5digeff10 years ago | 17 | // TODO: Make the port configurable via a launch argument |
| 18 | public static PORT = "8081"; | |
| 19 | public static HOST = `localhost:${Packager.PORT}`; | |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 20 | |
3fb37ad5unknown10 years ago | 21 | private projectPath: string; |
c9b4fa6cMeena Kunnathur Balakrishnan10 years ago | 22 | private packagerProcess: ChildProcess; |
3fb37ad5unknown10 years ago | 23 | |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 24 | private static JS_INJECTOR_FILENAME = "opn-main.js"; |
e952a6f3Meena Kunnathur Balakrishnan10 years ago | 25 | private static JS_INJECTOR_FILEPATH = path.resolve(path.dirname(path.dirname(__dirname)), "js-patched", Packager.JS_INJECTOR_FILENAME); |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 26 | private static NODE_MODULES_FODLER_NAME = "node_modules"; |
| 27 | private static OPN_PACKAGE_NAME = "opn"; | |
| 28 | private static REACT_NATIVE_PACKAGE_NAME = "react-native"; | |
| 29 | private static OPN_PACKAGE_MAIN_FILENAME = "index.js"; | |
| 30 | | |
2743f19cdlebu10 years ago | 31 | constructor(projectPath: string) { |
3fb37ad5unknown10 years ago | 32 | this.projectPath = projectPath; |
| 33 | } | |
| 34 | | |
10873e11digeff10 years ago | 35 | |
ec6e115dMeena Kunnathur Balakrishnan10 years ago | 36 | public start(outputChannel?: OutputChannel): Q.Promise<void> { |
6126d899Meena Kunnathur Balakrishnan10 years ago | 37 | let executedStartPackagerCmd = false; |
10873e11digeff10 years ago | 38 | return this.isRunning() |
| 39 | .then(running => { | |
| 40 | if (!running) { | |
| 41 | return this.monkeyPatchOpnForRNPackager() | |
| 42 | .then(() => { | |
| 43 | let args = ["--port", Packager.PORT]; | |
| 44 | let childEnvForDebugging = Object.assign({}, process.env, { REACT_DEBUGGER: "echo A debugger is not needed: " }); | |
| 45 | | |
| 46 | Log.logMessage("Starting Packager", outputChannel); | |
| 47 | // The packager will continue running while we debug the application, so we can"t | |
| 48 | // wait for this command to finish | |
3fb37ad5unknown10 years ago | 49 | |
10873e11digeff10 years ago | 50 | let spawnOptions = { env: childEnvForDebugging }; |
| 51 | | |
65fd8e85digeff10 years ago | 52 | // TODO #83 - PROMISE: We need to consume the result of this spawn |
10873e11digeff10 years ago | 53 | new CommandExecutor(this.projectPath).spawnReactPackager(args, spawnOptions, outputChannel).then((packagerProcess) => { |
| 54 | this.packagerProcess = packagerProcess; | |
| 55 | executedStartPackagerCmd = true; | |
| 56 | }); | |
b031edc7digeff10 years ago | 57 | }); |
6126d899Meena Kunnathur Balakrishnan10 years ago | 58 | } |
10873e11digeff10 years ago | 59 | }) |
| 60 | .then(() => | |
| 61 | this.awaitStart()) | |
| 62 | .then(() => { | |
| 63 | if (executedStartPackagerCmd) { | |
| 64 | Log.logMessage("Packager started.", outputChannel); | |
| 65 | } else { | |
| 66 | Log.logMessage("Packager is already running.", outputChannel); | |
| 67 | if (!outputChannel) { | |
65fd8e85digeff10 years ago | 68 | // TODO #83: This warning is printted incorrectly when the packager was started from the command palette. Fix it. |
ce591c62digeff10 years ago | 69 | Log.logWarning("Debugging is not supported if the React Native Packager is not started within VS Code. If debugging fails, please kill other active React Native packager processes and retry.", outputChannel); |
10873e11digeff10 years ago | 70 | } |
| 71 | } | |
| 72 | }); | |
3fb37ad5unknown10 years ago | 73 | } |
2e15926eMeena Kunnathur Balakrishnan10 years ago | 74 | |
a822ac85dlebu10 years ago | 75 | public stop(outputChannel?: OutputChannel): Q.Promise<void> { |
10873e11digeff10 years ago | 76 | return new CommandExecutor(this.projectPath).killReactPackager(this.packagerProcess, outputChannel).then(() => |
| 77 | this.packagerProcess = null); | |
2e15926eMeena Kunnathur Balakrishnan10 years ago | 78 | } |
b3a793eeNisheet Jain10 years ago | 79 | |
418d8ac5digeff10 years ago | 80 | public prewarmBundleCache(platform: string) { |
| 81 | let bundleURL = `http://${Packager.HOST}/index.${platform}.bundle`; | |
ea8a5f88digeff10 years ago | 82 | Log.logInternalMessage(LogLevel.Info, "About to get: " + bundleURL); |
418d8ac5digeff10 years ago | 83 | return new Request().request(bundleURL, true).then(() => { |
| 84 | Log.logMessage("The Bundle Cache was prewarmed."); | |
1b27c1ccJimmy Thomson10 years ago | 85 | }).catch(() => { |
| 86 | // The attempt to prefetch the bundle failed. | |
| 87 | // This may be because the bundle is not index.* so we shouldn't treat this as fatal. | |
418d8ac5digeff10 years ago | 88 | }); |
| 89 | } | |
2ec44b6dNisheet Jain10 years ago | 90 | |
b3a793eeNisheet Jain10 years ago | 91 | private isRunning(): Q.Promise<boolean> { |
| 92 | let statusURL = `http://${Packager.HOST}/status`; | |
| 93 | | |
| 94 | return new Request().request(statusURL) | |
| 95 | .then((body: string) => { | |
| 96 | return body === "packager-status:running"; | |
| 97 | }, | |
| 98 | (error: any) => { | |
| 99 | return false; | |
| 100 | }); | |
| 101 | } | |
| 102 | | |
| 103 | private awaitStart(retryCount = 30, delay = 2000): Q.Promise<boolean> { | |
| 104 | let pu: PromiseUtil = new PromiseUtil(); | |
| 105 | return pu.retryAsync(() => this.isRunning(), (running) => running, retryCount, delay, "Could not start the packager."); | |
| 106 | } | |
| 107 | | |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 108 | private findOpnPackage(): Q.Promise<string> { |
| 109 | try { | |
| 110 | let flatDependencyPackagePath = path.resolve(this.projectPath, Packager.NODE_MODULES_FODLER_NAME, | |
| 111 | Packager.OPN_PACKAGE_NAME, Packager.OPN_PACKAGE_MAIN_FILENAME); | |
| 112 | | |
| 113 | let nestedDependencyPackagePath = path.resolve(this.projectPath, Packager.NODE_MODULES_FODLER_NAME, | |
| 114 | Packager.REACT_NATIVE_PACKAGE_NAME, Packager.NODE_MODULES_FODLER_NAME, Packager.OPN_PACKAGE_NAME, Packager.OPN_PACKAGE_MAIN_FILENAME); | |
| 115 | | |
| 116 | let fsHelper = new Node.FileSystem(); | |
| 117 | | |
| 118 | // Attempt to find the 'opn' package directly under the project's node_modules folder (node4 +) | |
| 119 | // Else, attempt to find the package within the dependent node_modules of react-native package | |
61c4db14Meena Kunnathur Balakrishnan10 years ago | 120 | let possiblePaths = [flatDependencyPackagePath, nestedDependencyPackagePath]; |
e33d2cabdigeff10 years ago | 121 | return Q.any(possiblePaths.map(path => |
| 122 | fsHelper.exists(path).then(exists => | |
| 123 | exists | |
| 124 | ? Q.resolve(path) | |
| 125 | : Q.reject<string>("opn package location not found")))); | |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 126 | } catch (err) { |
b031edc7digeff10 years ago | 127 | console.error("The package \'opn\' was not found." + err); |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 128 | } |
| 129 | } | |
| 130 | | |
| 131 | private monkeyPatchOpnForRNPackager(): Q.Promise<void> { | |
61c4db14Meena Kunnathur Balakrishnan10 years ago | 132 | let opnPackage: Package; |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 133 | let destnFilePath: string; |
| 134 | | |
| 135 | // Finds the 'opn' package | |
| 136 | return this.findOpnPackage() | |
b031edc7digeff10 years ago | 137 | .then((opnIndexFilePath) => { |
| 138 | destnFilePath = opnIndexFilePath; | |
| 139 | // Read the package's "package.json" | |
| 140 | opnPackage = new Package(path.resolve(path.dirname(destnFilePath))); | |
| 141 | return opnPackage.parsePackageInformation(); | |
| 142 | }).then((packageJson) => { | |
| 143 | if (packageJson.main !== Packager.JS_INJECTOR_FILENAME) { | |
| 144 | // Copy over the patched 'opn' main file | |
| 145 | return new Node.FileSystem().copyFile(Packager.JS_INJECTOR_FILEPATH, path.resolve(path.dirname(destnFilePath), Packager.JS_INJECTOR_FILENAME)) | |
| 146 | .then(() => { | |
| 147 | // Write/over-write the "main" attribute with the new file | |
| 148 | return opnPackage.setMainFile(Packager.JS_INJECTOR_FILENAME); | |
| 149 | }); | |
| 150 | } | |
| 151 | }); | |
1011f2a8Meena Kunnathur Balakrishnan10 years ago | 152 | } |
eb113a1fdlebu10 years ago | 153 | } |