microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
4bd0c6691c098818360fd7858937bd9969fa5481

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/scriptImporter.ts

123lines · 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// TODO: I'll rename this file to scriptDownloader.ts before pushing the PR
5import fs = require("fs");
6import {Log} from "../utils/commands/log";
7import path = require("path");
8import Q = require("q");
9import {Request} from "../utils/node/request";
10import {SourceMapUtil} from "./sourceMap";
11import url = require("url");
12
13interface ISourceMap {
14 file: string;
15 sources: string[];
16 version: number;
17 names: string[];
18 mappings: string;
19 sourceRoot?: string;
20 sourcesContent?: string[];
21}
22
23interface DownloadedScript {
24 contents: string;
25 filepath: string;
26}
27
28export class ScriptImporter {
29 private sourcesStoragePath: string;
30 private sourceMapUtil: SourceMapUtil;
31
32 constructor(sourcesStoragePath: string) {
33 this.sourcesStoragePath = sourcesStoragePath;
34 this.sourceMapUtil = new SourceMapUtil();
35 }
36
37 public download(scriptUrlString: string): Q.Promise<DownloadedScript> {
38
39 // We'll get the source code, and store it locally to have a better debugging experience
40 return new Request().request(scriptUrlString, true).then(scriptBody => {
41 // Extract sourceMappingURL from body
42 let scriptUrl = url.parse(scriptUrlString); // scriptUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
43 let sourceMappingUrl = this.sourceMapUtil.getSourceMapURL(scriptUrl, scriptBody); // sourceMappingUrl = "http://localhost:8081/index.ios.map?platform=ios&dev=true"
44
45 let waitForSourceMapping = Q<void>(null);
46 if (sourceMappingUrl) {
47 /* handle source map - request it and store it locally */
48 waitForSourceMapping = this.writeSourceMap(sourceMappingUrl, scriptUrl)
49 .then(() => {
50 scriptBody = this.sourceMapUtil.updateScriptPaths(scriptBody, sourceMappingUrl);
51 });
52 }
53
54 return waitForSourceMapping
55 .then(() => this.writeScript(scriptBody, scriptUrl))
56 .then((scriptFilePath: string) => {
57 Log.logInternalMessage(`Script ${scriptUrlString} downloaded to ${scriptFilePath}`);
58 return { contents: scriptBody, filepath: scriptFilePath };
59 });
60 });
61 }
62
63 /**
64 * Updates paths in souce maps - VS code requires forward slash paths.
65 */
66 private updateSourceMapPaths(sourceMapBody: string, generatedCodeFilePath: string): string {
67 try {
68 let sourceMap = <ISourceMap>JSON.parse(sourceMapBody);
69 sourceMap.sources = sourceMap.sources.map(source => {
70 // Make all paths relative to the location of the source map
71 let relativeSourcePath = path.relative(this.sourcesStoragePath, source);
72 let sourceUrl = relativeSourcePath.replace(/\\/g, "/");
73 return sourceUrl;
74 });
75 // fixedSourceMapBody.sourceRoot = "..";
76 delete sourceMap.sourcesContent;
77 sourceMap.sourceRoot = "";
78 sourceMap.file = generatedCodeFilePath;
79 return JSON.stringify(sourceMap);
80 } catch (exception) {
81 return sourceMapBody;
82 }
83 }
84 /**
85 * Writes the script file to the project temporary location.
86 */
87 private writeScript(scriptBody: string, scriptUrl: url.Url): Q.Promise<String> {
88 return Q.fcall(() => {
89 let scriptFilePath = path.join(this.sourcesStoragePath, scriptUrl.pathname); // scriptFilePath = "$TMPDIR/index.ios.bundle"
90 this.writeTemporaryFileSync(scriptFilePath, scriptBody);
91 return scriptFilePath;
92 });
93 }
94
95 /**
96 * Writes the source map file to the project temporary location.
97 */
98 private writeSourceMap(sourceMapUrl: url.Url, scriptUrl: url.Url): Q.Promise<void> {
99 return new Request().request(sourceMapUrl.href, true)
100 .then((sourceMapBody: string) => {
101 let sourceMappingLocalPath = path.join(this.sourcesStoragePath, sourceMapUrl.pathname); // sourceMappingLocalPath = "$TMPDIR/index.ios.map"
102 let scriptFileRelativePath = path.basename(scriptUrl.pathname); // scriptFileRelativePath = "index.ios.bundle"
103 this.writeTemporaryFileSync(sourceMappingLocalPath, this.updateSourceMapPaths(sourceMapBody, scriptFileRelativePath));
104 });
105 }
106
107 private writeTemporaryFileSync(filename: string, data: string): Q.Promise<void> {
108 let writeFile = Q.nfbind<void>(fs.writeFile);
109
110 return writeFile(filename, data)
111 .then(() => this.scheduleTemporaryFileCleanUp(filename));
112 }
113
114 private scheduleTemporaryFileCleanUp(filename: string): void {
115 process.on("exit", function() {
116 let unlink = Q.nfbind<void>(fs.unlink);
117 unlink(filename)
118 .then(() => {
119 Log.logMessage("Succesfully cleaned temporary file: " + filename);
120 });
121 });
122 }
123}
124