microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
f29c7bfe4a082d46856c377fa5966b80f4e52c16

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/intellisenseHelper.ts

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