microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
bedf110fbb4cf82fb995f4cf2770e8339d5adbea

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/scriptImporter.ts

109lines · modecode

1import fs = require("fs");
2import http = require("http");
3import {Log} from "../utils/commands/log";
4import path = require("path");
5import Q = require("q");
6import request = require("request");
7import url = require("url");
8import vm = require("vm");
9
10interface ISourceMap {
11 file: string;
12 sources: string[];
13 version: number;
14 names: string[];
15 mappings: string;
16 sourceRoot?: string;
17 sourcesContent?: string[];
18}
19
20export class ScriptImporter {
21 private projectRootPath: string;
22 private bundleFolderPath: string;
23
24 constructor(projectRootPath: string) {
25 this.projectRootPath = projectRootPath;
26 // We put the source code inside the workspace, because VS Code doesn't seem to support source mapping if we don't do that
27 this.bundleFolderPath = path.join(this.projectRootPath, ".vscode");
28 }
29
30 private fixSourceMap(sourceMapBody: string, generatedCodeFilePath: string): string {
31 try {
32 let sourceMap = <ISourceMap> JSON.parse(sourceMapBody);
33 sourceMap.sources = sourceMap.sources.map(source => {
34 // Make all paths relative to the location of the source map
35 let relativeSourcePath = path.relative(this.bundleFolderPath, source);
36 let sourceUrl = relativeSourcePath.replace(/\\/g, "/");
37 return sourceUrl;
38 });
39 // fixedSourceMapBody.sourceRoot = "..";
40 delete sourceMap.sourcesContent;
41 sourceMap.sourceRoot = "";
42 sourceMap.file = generatedCodeFilePath;
43 return JSON.stringify(sourceMap);
44 } catch (exception) {
45 return sourceMapBody;
46 }
47 }
48
49 public import(scriptUrl: string): Q.Promise<void> {
50
51 // We'll get the source code, and store it locally to have a better debugging experience
52 return this.request(scriptUrl).then(scriptBody => {
53 // Extract sourceMappingURL from body
54 let parsedUrl = url.parse(scriptUrl); // scriptUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
55 let sourceMappingRelativeUrl = this.sourceMapRelativeUrl(scriptBody); // sourceMappingRelativeUrl = "/index.ios.map?platform=ios&dev=true"
56 let sourceMappingUrl = parsedUrl.protocol + "//" + parsedUrl.host + sourceMappingRelativeUrl; // sourceMappingUrl = "http://localhost:8081/index.ios.map?platform=ios&dev=true"
57
58 // We'll get the source map, and story it locally, so we can debug the original files instead of the bundle
59 return this.request(sourceMappingUrl).then(sourceMapBody => {
60 let sourceMappingRelativePath = url.parse(sourceMappingRelativeUrl).pathname.substr(1); // sourceMappingRelativePath = "index.ios.map?platform=ios&dev=true"
61 let sourceMappingLocalPath = path.join(this.bundleFolderPath, sourceMappingRelativePath); // sourceMappingLocalPath = "$TMPDIR/index.ios.map?platform=ios&dev=true"
62 let scriptFileRelativePath = path.basename(parsedUrl.pathname); // scriptFileRelativePath = "index.ios.bundle"
63 this.writeTemporaryFileSync(sourceMappingLocalPath, this.fixSourceMap(sourceMapBody, scriptFileRelativePath));
64 // Update the body with the new location of the source map on storage
65 scriptBody = scriptBody.replace(/^\/\/# sourceMappingURL=(.*)$/m, "//# sourceMappingURL=" + sourceMappingRelativePath);
66 }).then(() => { // TODO: Figure out how to handle the case that the source mapping fails.
67 let scriptFilePath = path.join(this.bundleFolderPath, parsedUrl.pathname); // scriptFilePath = "$TMPDIR/index.ios.bundle"
68 this.writeTemporaryFileSync(scriptFilePath, scriptBody);
69
70 // The next line converts to any due to the incorrect typing on node.d.ts of vm.runInThisContext
71 vm.runInThisContext(scriptBody, <any>{ filename: scriptFilePath });
72 Log.logMessage("Imported script at " + scriptUrl + " locally stored on " + this.bundleFolderPath);
73 });
74 });
75 }
76
77 private writeTemporaryFileSync(filename: string, data: string) {
78 fs.writeFileSync(filename, data);
79 this.scheduleTemporaryFileCleanUp(filename);
80 }
81 private scheduleTemporaryFileCleanUp(filename: string) {
82 process.on("exit", function (){
83 fs.unlinkSync(filename);
84 Log.logMessage("Succesfully cleaned temporary file: " + filename);
85 });
86 }
87
88 private sourceMapRelativeUrl(body: any) {
89 let match = body.match(/^\/\/# sourceMappingURL=(.*)$/m);
90 // If match is null, the body doesn't contain the source map
91 return match ? match[1] : null;
92 }
93
94 private request(uri: string): Q.Promise<any> {
95 let result = Q.defer<any>();
96 request(uri, function (error: any, response: http.IncomingMessage, body: any) {
97 if (!error) {
98 if (response.statusCode === 200) {
99 result.resolve(body);
100 } else {
101 result.reject(body);
102 }
103 } else {
104 result.reject(error);
105 }
106 });
107 return result.promise;
108 }
109}
110