microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8a9925ee8b659b3f9dba8a521555c8ee58fd8f31

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/intellisenseHelper.ts

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