microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
89973b41e393a344a45388d2e4b7318dfd5888d6

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/scriptImporter.ts

159lines · modeblame

a31b007cunknown10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4677921cdigeff10 years ago4// TODO: I'll rename this file to scriptDownloader.ts before pushing the PR
3fb37ad5unknown10 years ago5import fs = require("fs");
bedf110funknown10 years ago6import {Log} from "../utils/commands/log";
3fb37ad5unknown10 years ago7import path = require("path");
8import Q = require("q");
e00f7325unknown10 years ago9import {Request} from "../utils/node/request";
3fb37ad5unknown10 years ago10import url = require("url");
11
12interface ISourceMap {
13file: string;
14sources: string[];
15version: number;
16names: string[];
17mappings: string;
18sourceRoot?: string;
19sourcesContent?: string[];
20}
21
4677921cdigeff10 years ago22interface DownloadedScript {
23contents: string;
24filepath: string;
25}
26
3fb37ad5unknown10 years ago27export class ScriptImporter {
4677921cdigeff10 years ago28private sourcesStoragePath: string;
3fb37ad5unknown10 years ago29
4677921cdigeff10 years ago30constructor(sourcesStoragePath: string) {
31this.sourcesStoragePath = sourcesStoragePath;
3fb37ad5unknown10 years ago32}
33
4677921cdigeff10 years ago34public download(scriptUrlString: string): Q.Promise<DownloadedScript> {
e00f7325unknown10 years ago35
36// We'll get the source code, and store it locally to have a better debugging experience
37return new Request().request(scriptUrlString, true).then(scriptBody => {
38// Extract sourceMappingURL from body
39let scriptUrl = url.parse(scriptUrlString); // scriptUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
40let sourceMappingUrl = this.getSourceMapURL(scriptUrl, scriptBody); // sourceMappingUrl = "http://localhost:8081/index.ios.map?platform=ios&dev=true"
41
4677921cdigeff10 years ago42let waitForSourceMapping = Q<void>(null);
e00f7325unknown10 years ago43if (sourceMappingUrl) {
44/* handle source map - request it and store it locally */
4677921cdigeff10 years ago45waitForSourceMapping = this.writeSourceMap(sourceMappingUrl, scriptUrl)
3af9a124unknown10 years ago46.then(() => {
47scriptBody = this.updateScriptPaths(scriptBody, sourceMappingUrl);
4677921cdigeff10 years ago48});
e00f7325unknown10 years ago49}
4677921cdigeff10 years ago50
51return waitForSourceMapping
52.then(() => this.writeScript(scriptBody, scriptUrl))
53.then((scriptFilePath: string) => {
54Log.logInternalMessage(`Script ${scriptUrlString} downloaded to ${scriptFilePath}`);
55return { contents: scriptBody, filepath: scriptFilePath };
56});
e00f7325unknown10 years ago57});
58}
59
60/**
61* Updates source map URLs in the script body.
62*/
63private updateScriptPaths(scriptBody: string, sourceMappingUrl: url.Url) {
64// Update the body with the new location of the source map on storage.
8500bbc4dlebu10 years ago65return scriptBody.replace(/^\/\/# sourceMappingURL=(.*)$/m, "//# sourceMappingURL=" + path.basename(sourceMappingUrl.pathname));
e00f7325unknown10 years ago66}
67
68/**
69* Updates paths in souce maps - VS code requires forward slash paths.
70*/
71private updateSourceMapPaths(sourceMapBody: string, generatedCodeFilePath: string): string {
3fb37ad5unknown10 years ago72try {
e00f7325unknown10 years ago73let sourceMap = <ISourceMap>JSON.parse(sourceMapBody);
3fb37ad5unknown10 years ago74sourceMap.sources = sourceMap.sources.map(source => {
75// Make all paths relative to the location of the source map
4677921cdigeff10 years ago76let relativeSourcePath = path.relative(this.sourcesStoragePath, source);
3fb37ad5unknown10 years ago77let sourceUrl = relativeSourcePath.replace(/\\/g, "/");
78return sourceUrl;
79});
80// fixedSourceMapBody.sourceRoot = "..";
81delete sourceMap.sourcesContent;
82sourceMap.sourceRoot = "";
83sourceMap.file = generatedCodeFilePath;
84return JSON.stringify(sourceMap);
85} catch (exception) {
86return sourceMapBody;
87}
88}
e00f7325unknown10 years ago89/**
90* Writes the script file to the project temporary location.
91*/
92private writeScript(scriptBody: string, scriptUrl: url.Url): Q.Promise<String> {
93return Q.fcall(() => {
4677921cdigeff10 years ago94let scriptFilePath = path.join(this.sourcesStoragePath, scriptUrl.pathname); // scriptFilePath = "$TMPDIR/index.ios.bundle"
e00f7325unknown10 years ago95this.writeTemporaryFileSync(scriptFilePath, scriptBody);
fb2bae06Daniel10 years ago96// Log.logMessage("Imported script at " + scriptUrl.path + " locally stored on " + scriptFilePath);
e00f7325unknown10 years ago97return scriptFilePath;
3fb37ad5unknown10 years ago98});
99}
100
e00f7325unknown10 years ago101/**
102* Writes the source map file to the project temporary location.
103*/
104private writeSourceMap(sourceMapUrl: url.Url, scriptUrl: url.Url): Q.Promise<void> {
105return new Request().request(sourceMapUrl.href, true)
106.then((sourceMapBody: string) => {
4677921cdigeff10 years ago107let sourceMappingLocalPath = path.join(this.sourcesStoragePath, sourceMapUrl.pathname); // sourceMappingLocalPath = "$TMPDIR/index.ios.map"
e00f7325unknown10 years ago108let scriptFileRelativePath = path.basename(scriptUrl.pathname); // scriptFileRelativePath = "index.ios.bundle"
109this.writeTemporaryFileSync(sourceMappingLocalPath, this.updateSourceMapPaths(sourceMapBody, scriptFileRelativePath));
110});
3fb37ad5unknown10 years ago111}
e00f7325unknown10 years ago112
113/**
114* Given a script body and URL, this method parses the body and finds the corresponding source map URL.
115* If the source map URL is not found in the body in the expected form, null is returned.
116*/
117private getSourceMapURL(scriptUrl: url.Url, scriptBody: string): url.Url {
118let result: url.Url = null;
119
120// scriptUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
121let sourceMappingRelativeUrl = this.sourceMapRelativeUrl(scriptBody); // sourceMappingRelativeUrl = "/index.ios.map?platform=ios&dev=true"
122if (sourceMappingRelativeUrl) {
fb2bae06Daniel10 years ago123let sourceMappingUrl = url.parse(sourceMappingRelativeUrl);
124sourceMappingUrl.protocol = scriptUrl.protocol;
125sourceMappingUrl.host = scriptUrl.host;
126// parse() repopulates all the properties of the URL
127result = url.parse(url.format(sourceMappingUrl));
e00f7325unknown10 years ago128}
129
130return result;
3fb37ad5unknown10 years ago131}
132
e00f7325unknown10 years ago133/**
134* Parses the body of a script searching for a source map URL.
135* Returns the first match if found, null otherwise.
136*/
137private sourceMapRelativeUrl(body: string) {
3fb37ad5unknown10 years ago138let match = body.match(/^\/\/# sourceMappingURL=(.*)$/m);
139// If match is null, the body doesn't contain the source map
140return match ? match[1] : null;
141}
142
2f10b3adunknown10 years ago143private writeTemporaryFileSync(filename: string, data: string): Q.Promise<void> {
144let writeFile = Q.nfbind<void>(fs.writeFile);
145
146return writeFile(filename, data)
147.then(() => this.scheduleTemporaryFileCleanUp(filename));
e00f7325unknown10 years ago148}
149
2f10b3adunknown10 years ago150private scheduleTemporaryFileCleanUp(filename: string): void {
e00f7325unknown10 years ago151process.on("exit", function() {
2f10b3adunknown10 years ago152let unlink = Q.nfbind<void>(fs.unlink);
153unlink(filename)
154.then(() => {
155Log.logMessage("Succesfully cleaned temporary file: " + filename);
156});
3fb37ad5unknown10 years ago157});
158}
159}