microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
eba0de58017cd198f69bffca9be3d8e0e4df4d6a

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/scriptImporter.ts

139lines · 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 {FileSystem} from "../common/node/fileSystem";
5import { logger } from "vscode-chrome-debug-core";
6import { ensurePackagerRunning } from "../common/packagerStatus";
7import path = require("path");
8import Q = require("q");
9import {Request} from "../common/node/request";
10import {SourceMapUtil} from "./sourceMap";
11import url = require("url");
12import * as semver from "semver";
13import {ReactNativeProjectHelper} from "../common/reactNativeProjectHelper";
14
15export interface DownloadedScript {
16 contents: string;
17 filepath: string;
18}
19
20interface IStrictUrl extends url.Url {
21 pathname: string;
22 href: string;
23}
24
25export class ScriptImporter {
26 public static DEBUGGER_WORKER_FILE_BASENAME = "debuggerWorker";
27 public static DEBUGGER_WORKER_FILENAME = ScriptImporter.DEBUGGER_WORKER_FILE_BASENAME + ".js";
28 private packagerAddress: string;
29 private packagerPort: number;
30 private sourcesStoragePath: string;
31 private packagerRemoteRoot?: string;
32 private packagerLocalRoot?: string;
33 private sourceMapUtil: SourceMapUtil;
34
35 constructor(packagerAddress: string, packagerPort: number, sourcesStoragePath: string, packagerRemoteRoot?: string, packagerLocalRoot?: string) {
36 this.packagerAddress = packagerAddress;
37 this.packagerPort = packagerPort;
38 this.sourcesStoragePath = sourcesStoragePath;
39 this.packagerRemoteRoot = packagerRemoteRoot;
40 this.packagerLocalRoot = packagerLocalRoot;
41 this.sourceMapUtil = new SourceMapUtil();
42 }
43
44 public downloadAppScript(scriptUrlString: string, projectRootPath: string): Q.Promise<DownloadedScript> {
45 const parsedScriptUrl = url.parse(scriptUrlString);
46 const overriddenScriptUrlString = (parsedScriptUrl.hostname === "localhost") ? this.overridePackagerPort(scriptUrlString) : scriptUrlString;
47 // We'll get the source code, and store it locally to have a better debugging experience
48 return Request.request(overriddenScriptUrlString, true).then(scriptBody => {
49 return ReactNativeProjectHelper.getReactNativeVersion(projectRootPath).then(rnVersion => {
50 // unfortunatelly Metro Bundler is broken in RN 0.54.x versions, so use this workaround unless it will be fixed
51 // https://github.com/facebook/metro/issues/147
52 // https://github.com/Microsoft/vscode-react-native/issues/660
53 if (ReactNativeProjectHelper.getRNVersionsWithBrokenMetroBundler().indexOf(rnVersion) >= 0) {
54 let noSourceMappingUrlGenerated = scriptBody.match(/sourceMappingURL=/g) === null;
55 if (noSourceMappingUrlGenerated) {
56 let sourceMapPathUrl = overriddenScriptUrlString.replace("bundle", "map");
57 scriptBody = this.sourceMapUtil.appendSourceMapPaths(scriptBody, sourceMapPathUrl);
58 }
59 }
60
61 // Extract sourceMappingURL from body
62 let scriptUrl = <IStrictUrl>url.parse(overriddenScriptUrlString); // scriptUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
63 let sourceMappingUrl = this.sourceMapUtil.getSourceMapURL(scriptUrl, scriptBody); // sourceMappingUrl = "http://localhost:8081/index.ios.map?platform=ios&dev=true"
64
65 let waitForSourceMapping = Q<void>(void 0);
66 if (sourceMappingUrl) {
67 /* handle source map - request it and store it locally */
68 waitForSourceMapping = this.writeAppSourceMap(sourceMappingUrl, scriptUrl)
69 .then(() => {
70 scriptBody = this.sourceMapUtil.updateScriptPaths(scriptBody, <IStrictUrl>sourceMappingUrl);
71 });
72 }
73
74 return waitForSourceMapping
75 .then(() => this.writeAppScript(scriptBody, scriptUrl))
76 .then((scriptFilePath: string) => {
77 logger.verbose(`Script ${overriddenScriptUrlString} downloaded to ${scriptFilePath}`);
78 return { contents: scriptBody, filepath: scriptFilePath };
79 });
80 });
81 });
82 }
83
84 public downloadDebuggerWorker(sourcesStoragePath: string, projectRootPath: string): Q.Promise<void> {
85 const errPackagerNotRunning = new RangeError(`Cannot attach to packager. Are you sure there is a packager and it is running in the port ${this.packagerPort}? If your packager is configured to run in another port make sure to add that to the setting.json.`);
86
87 return ensurePackagerRunning(this.packagerAddress, this.packagerPort, errPackagerNotRunning)
88 .then(() => {
89 return ReactNativeProjectHelper.getReactNativeVersion(projectRootPath);
90 })
91 .then((rnVersion: string) => {
92 let newPackager = "";
93 const isHaulProject = ReactNativeProjectHelper.isHaulProject(projectRootPath);
94 if (!semver.valid(rnVersion) /*Custom RN implementations should support new packager*/ || (semver.gte(rnVersion, "0.50.0") && !isHaulProject)) {
95 newPackager = "debugger-ui/";
96 }
97 let debuggerWorkerURL = `http://${this.packagerAddress}:${this.packagerPort}/${newPackager}${ScriptImporter.DEBUGGER_WORKER_FILENAME}`;
98 let debuggerWorkerLocalPath = path.join(sourcesStoragePath, ScriptImporter.DEBUGGER_WORKER_FILENAME);
99 logger.verbose("About to download: " + debuggerWorkerURL + " to: " + debuggerWorkerLocalPath);
100
101 return Request.request(debuggerWorkerURL, true)
102 .then((body: string) => {
103 return new FileSystem().writeFile(debuggerWorkerLocalPath, body);
104 });
105 });
106 }
107
108 /**
109 * Writes the script file to the project temporary location.
110 */
111 private writeAppScript(scriptBody: string, scriptUrl: IStrictUrl): Q.Promise<String> {
112 let scriptFilePath = path.join(this.sourcesStoragePath, path.basename(scriptUrl.pathname)); // scriptFilePath = "$TMPDIR/index.ios.bundle"
113 return new FileSystem().writeFile(scriptFilePath, scriptBody)
114 .then(() => scriptFilePath);
115 }
116
117 /**
118 * Writes the source map file to the project temporary location.
119 */
120 private writeAppSourceMap(sourceMapUrl: IStrictUrl, scriptUrl: IStrictUrl): Q.Promise<void> {
121 return Request.request(sourceMapUrl.href, true)
122 .then((sourceMapBody: string) => {
123 let sourceMappingLocalPath = path.join(this.sourcesStoragePath, path.basename(sourceMapUrl.pathname)); // sourceMappingLocalPath = "$TMPDIR/index.ios.map"
124 let scriptFileRelativePath = path.basename(scriptUrl.pathname); // scriptFileRelativePath = "index.ios.bundle"
125 let updatedContent = this.sourceMapUtil.updateSourceMapFile(sourceMapBody, scriptFileRelativePath, this.sourcesStoragePath, this.packagerRemoteRoot, this.packagerLocalRoot);
126 return new FileSystem().writeFile(sourceMappingLocalPath, updatedContent);
127 });
128 }
129
130 /**
131 * Changes the port of the url to be the one configured as this.packagerPort
132 */
133 private overridePackagerPort(urlToOverride: string): string {
134 let components = url.parse(urlToOverride);
135 components.port = this.packagerPort.toString();
136 delete components.host; // We delete the host, if not the port change will be ignored
137 return url.format(components);
138 }
139}
140