microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e15bc470c1fec84fa709b916f1a81b89a7245c56

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/scriptImporter.ts

108lines · modecode

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