microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b1df983ea647dac05f1c141dc7b476ef60c350ab

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/intellisenseHelper.ts

271lines · 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 * as child_process from "child_process";
6import * as os from "os";
7import * as path from "path";
8import * as Q from "q";
9import * as vscode from "vscode";
10import * as semver from "semver";
11import {Telemetry} from "../common/telemetry";
12import {TelemetryHelper} from "../common/telemetryHelper";
13import {CommandExecutor} from "../common/commandExecutor";
14import {TsConfigHelper} from "./tsconfigHelper";
15import {SettingsHelper} from "./settingsHelper";
16import {Log, LogLevel} from "../common/log";
17
18
19interface InstallProps {
20 installed: boolean;
21 version: string;
22}
23
24export class IntellisenseHelper {
25
26 private static s_typeScriptVersion = "1.8.2"; // preferred version of TypeScript for legacy VSCode installs
27 private static s_vsCodeVersion = "0.10.10-insider"; // preferred version of VSCode (current is 0.10.9, 0.10.10-insider+ will include native TypeScript support)
28 // note: semver considers "x.x.x-<string>" to be < "x.x.x"" - so we include insider here as the
29 // insider build is less than the release build of 0.10.10 and we will support it.
30
31 /**
32 * Helper method that configures the workspace for Salsa intellisense.
33 */
34 public static setupReactNativeIntellisense(): Q.Promise<void> {
35 // Telemetry - Send Salsa Environment setup information
36 const tsSalsaEnvSetup = TelemetryHelper.createTelemetryEvent("RNIntellisense");
37 TelemetryHelper.addTelemetryEventProperty(tsSalsaEnvSetup, "TsSalsaEnvSetup", !!process.env.VSCODE_TSJS, false);
38 Telemetry.send(tsSalsaEnvSetup);
39
40 const configureWorkspace = Q({})
41 .then(() => TsConfigHelper.allowJs(true))
42 .then(() => TsConfigHelper.addExcludePaths(["node_modules"]))
43 .then(() => IntellisenseHelper.installReactNativeTypings());
44
45 // The actions taken in the promise chain below may result in requring a restart.
46 const configureTypescript = Q(false)
47 .then((isRestartRequired: boolean) => IntellisenseHelper.enableSalsa(isRestartRequired))
48 .then((isRestartRequired: boolean) => IntellisenseHelper.verifyInstallTypeScript(isRestartRequired))
49 .then((isRestartRequired: boolean) => IntellisenseHelper.configureWorkspaceSettings(isRestartRequired))
50 .then((isRestartRequired: boolean) => IntellisenseHelper.warnIfRestartIsRequired(isRestartRequired))
51 .catch((err: any) => {
52 Log.logError("Error while setting up IntelliSense: " + err);
53 return Q.reject<void>(err);
54 });
55
56 /* TODO #83: Refactor this code to
57 Q.all([enableSalsa(), installTypescript(), configureWorkspace()])
58 .then((result) => warnIfRestartIsRequired(result.any((x) => x)))
59 */
60 return Q.all([configureWorkspace, configureTypescript]).then(() => { });
61 }
62
63 /**
64 * Helper method that install typings for React Native.
65 */
66 public static installReactNativeTypings(): Q.Promise<void> {
67 let reactTypingsSource = path.resolve(__dirname, "..", "..", "ReactTypings");
68 let reactTypingsDest = path.resolve(vscode.workspace.rootPath, ".vscode", "typings");
69 let fileSystem = new FileSystem();
70
71 return fileSystem.copyRecursive(reactTypingsSource, reactTypingsDest);
72 }
73
74 /**
75 * Helper method that verifies the correct version of TypeScript is installed.
76 * If using a newer version of VSCode TypeScript is installed by default and no
77 * action is needed. If using an older version, verify that the correct TS version is
78 * installed, if not install it.
79 */
80 public static verifyInstallTypeScript(isRestartRequired: boolean): Q.Promise<boolean> {
81
82 if (IntellisenseHelper.isSalsaSupported()) {
83 // this is the correct version of vscode, which includes TypeScript (Salsa) support, nothing to do here
84 return Q.resolve<boolean>(isRestartRequired);
85 }
86
87 return IntellisenseHelper.getInstalledTypeScriptVersion()
88 .then(function(installProps: InstallProps) {
89
90 if (installProps.installed === true) {
91
92 if (semver.neq(IntellisenseHelper.s_typeScriptVersion, installProps.version)) {
93 Log.logInternalMessage(LogLevel.Debug, "TypeScript is installed with the wrong version: " + installProps.version);
94 return true;
95 } else {
96 Log.logInternalMessage(LogLevel.Debug, "Installed TypeScript version is correct");
97 return false;
98 }
99 } else {
100 Log.logInternalMessage(LogLevel.Debug, "TypeScript is not installed");
101 return true;
102 }
103 })
104 .then((install: boolean) => {
105
106 if (install) {
107 let installPath: string = path.resolve(IntellisenseHelper.getUserHomePath(), ".vscode");
108 let runArguments: string[] = [];
109 runArguments.push("install");
110 runArguments.push("--prefix " + installPath);
111 runArguments.push("typescript@" + IntellisenseHelper.s_typeScriptVersion);
112
113 return new CommandExecutor(installPath).spawnAndWaitForCompletion("npm", runArguments)
114 .then(() => {
115 return true;
116 })
117 .catch((err: any) => {
118 Log.logError("Error attempting to install TypeScript: " + err);
119 return Q.reject<boolean>(err);
120 });
121
122 } else {
123 return isRestartRequired;
124 }
125 });
126 }
127
128 public static getUserHomePath(): string {
129 let homeDirectory: string = "";
130
131 if (os.type() === "Darwin") {
132 homeDirectory = process.env.HOME;
133 } else if (os.type() === "Windows_NT") {
134 homeDirectory = process.env.USERPROFILE;
135 }
136
137 return homeDirectory;
138 }
139
140 public static configureWorkspaceSettings(isRestartRequired: boolean): Q.Promise<boolean> {
141 let typeScriptLibPath: string = path.resolve(IntellisenseHelper.getTypeScriptInstallPath(), "lib");
142
143 return SettingsHelper.getTypeScriptTsdk()
144 .then((tsdkPath: string) => {
145
146 if (IntellisenseHelper.isSalsaSupported()) {
147 if (tsdkPath !== null &&
148 tsdkPath === typeScriptLibPath) {
149 // Note: In previous releases of VSCode (< 0.10.10) the Salsa TypeScript
150 // IntelliSense was not enabled by default, this extension would install
151 // Salsa itself, and update the settings to point at that. Here we
152 // attempt to reset that value to null if it still points to the previous
153 // installed (and no longer valid) version of TypeScript.
154 return SettingsHelper.removeTypeScriptTsdk()
155 .then(() => { return true; });
156 }
157 } else {
158 if (tsdkPath === null) {
159 return SettingsHelper.setTypeScriptTsdk(typeScriptLibPath)
160 .then(() => { return true; });
161 }
162 }
163
164 return isRestartRequired;
165 });
166 }
167
168 public static warnIfRestartIsRequired(isRestartRequired: boolean): Q.Promise<void> {
169 if (isRestartRequired) {
170 vscode.window.showInformationMessage("React Native intellisense was successfully configured for this project. Restart to enable it.");
171 }
172
173 return;
174 }
175
176 /**
177 * Helper method that sets the environment variable and informs the user they need to restart
178 * in order to enable the Salsa intellisense.
179 */
180 public static enableSalsa(isRestartRequired: boolean): Q.Promise<boolean> {
181 if (!process.env.VSCODE_TSJS) {
182 let setEnvironmentVariableCommand: string = "";
183 if (os.type() === "Darwin") {
184 setEnvironmentVariableCommand = "launchctl setenv VSCODE_TSJS 1";
185 } else if (os.type() === "Windows") {
186 setEnvironmentVariableCommand = "setx VSCODE_TSJS 1";
187 }
188
189 return Q({})
190 .then(() => Q.nfcall(child_process.exec, setEnvironmentVariableCommand))
191 .then(() => { return true; });
192 }
193
194 return Q(isRestartRequired);
195 }
196
197 /**
198 * Simple check to see if the TypeScript package is in the expected location (where we installed it)
199 */
200 private static isTypeScriptInstalled(): Q.Promise<boolean> {
201 let fileSystem: FileSystem = new FileSystem();
202 let installPath: string = path.join(IntellisenseHelper.getTypeScriptInstallPath(), "lib");
203 return fileSystem.exists(installPath);
204 }
205
206 /**
207 * Checks for the existance of our installed TypeScript package, if it exists also determine its version
208 */
209 private static getInstalledTypeScriptVersion(): Q.Promise<InstallProps> {
210 return IntellisenseHelper.isTypeScriptInstalled()
211 .then((installed: boolean) => {
212 let installProps: InstallProps = {
213 installed: installed,
214 version: ""
215 };
216
217 if (installed === true) {
218 Log.logInternalMessage(LogLevel.Debug, "TypeScript is installed - checking version");
219 return IntellisenseHelper.readPackageJson()
220 .then((version: string) => {
221 installProps.version = version;
222 return installProps;
223 });
224 } else {
225 return installProps;
226 }
227 });
228 }
229
230 /**
231 * Read the package.json from the TypeScript install path and return the version if it's available
232 */
233 private static readPackageJson(): Q.Promise<string> {
234 let packageFilePath: string = path.join(IntellisenseHelper.getTypeScriptInstallPath(), "package.json");
235 let fileSystem = new FileSystem();
236
237 return fileSystem.exists(packageFilePath)
238 .then(function(exists: boolean): Q.Promise<string> {
239 if (!exists) {
240 return Q.reject<string>("package.json not found at:" + packageFilePath);
241 }
242
243 return fileSystem.readFile(packageFilePath, "utf-8");
244 })
245 .then(function(jsonContents: string): Q.Promise<any> {
246 let data = JSON.parse(jsonContents);
247 return data.version;
248 })
249 .catch((err: any) => {
250 Log.logError("Error while procesing package.json: " + err);
251 return "0.0.0";
252 });
253 }
254
255 /**
256 * Simple helper to get the TypeScript install path
257 */
258 private static getTypeScriptInstallPath(): string {
259
260 let codePath: string = path.resolve(IntellisenseHelper.getUserHomePath(), ".vscode");
261 let typeScriptLibPath: string = path.join(codePath, "node_modules", "typescript");
262 return typeScriptLibPath;
263 }
264
265 /**
266 * Simple helper to determine if the current version of VSCode supports TypeScript (Salsa) or better
267 */
268 private static isSalsaSupported(): boolean {
269 return semver.gte(vscode.version, IntellisenseHelper.s_vsCodeVersion, true);
270 }
271}