microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.8.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/exponent/exponentHelper.ts

319lines · modeblame

1c32fe84Patricio Beltran9 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
94cd5149Artem Egorov8 years ago4/// <reference path="exponentHelper.d.ts" />
5
1c32fe84Patricio Beltran9 years ago6import * as path from "path";
7import * as Q from "q";
7059d307Patricio Beltran9 years ago8import * as XDL from "./xdlInterface";
0a68f8dbArtem Egorov8 years ago9import { Package } from "../../common/node/package";
10import { ReactNativeProjectHelper } from "../../common/reactNativeProjectHelper";
11import { FileSystem } from "../../common/node/fileSystem";
12import {OutputChannelLogger} from "../log/OutputChannelLogger";
94cd5149Artem Egorov8 years ago13import stripJSONComments = require("strip-json-comments");
d7d405aeYuri Skorokhodov7 years ago14import * as nls from "vscode-nls";
15import { ErrorHelper } from "../../common/error/errorHelper";
16import { InternalErrorCode } from "../../common/error/internalErrorCode";
17const localize = nls.loadMessageBundle();
1c32fe84Patricio Beltran9 years ago18
94cd5149Artem Egorov8 years ago19const APP_JSON = "app.json";
20const EXP_JSON = "exp.json";
1c32fe84Patricio Beltran9 years ago21
22const EXPONENT_INDEX = "exponentIndex.js";
94cd5149Artem Egorov8 years ago23const DEFAULT_EXPONENT_INDEX = "index.js";
1c32fe84Patricio Beltran9 years ago24const DEFAULT_IOS_INDEX = "index.ios.js";
25const DEFAULT_ANDROID_INDEX = "index.android.js";
26
94cd5149Artem Egorov8 years ago27const DBL_SLASHES = /\\/g;
1c32fe84Patricio Beltran9 years ago28
29export class ExponentHelper {
38edb09eAlexander Sorokin9 years ago30private workspaceRootPath: string;
94cd5149Artem Egorov8 years ago31private projectRootPath: string;
2956dba4Yuri Skorokhodov7 years ago32private fs: FileSystem;
a57e740bPatricio Beltran9 years ago33private hasInitialized: boolean;
0a68f8dbArtem Egorov8 years ago34private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
1c32fe84Patricio Beltran9 years ago35
2956dba4Yuri Skorokhodov7 years ago36public constructor(workspaceRootPath: string, projectRootPath: string, fs: FileSystem = new FileSystem()) {
38edb09eAlexander Sorokin9 years ago37this.workspaceRootPath = workspaceRootPath;
38this.projectRootPath = projectRootPath;
2956dba4Yuri Skorokhodov7 years ago39this.fs = fs;
a57e740bPatricio Beltran9 years ago40this.hasInitialized = false;
41// Constructor is slim by design. This is to add as less computation as possible
42// to the initialization of the extension. If a public method is added, make sure
b0af599cJimmy Thomson9 years ago43// to call this.lazilyInitialize() at the begining of the code to be sure all variables
a57e740bPatricio Beltran9 years ago44// are correctly initialized.
1c32fe84Patricio Beltran9 years ago45}
46
d1d77244Jimmy Thomson9 years ago47public configureExponentEnvironment(): Q.Promise<void> {
48this.lazilyInitialize();
d7d405aeYuri Skorokhodov7 years ago49this.logger.info(localize("MakingSureYourProjectUsesCorrectExponentDependencies", "Making sure your project uses the correct dependencies for Expo. This may take a while..."));
50this.logger.logStream(localize("CheckingIfThisIsExpoApp", "Checking if this is Expo app."));
2956dba4Yuri Skorokhodov7 years ago51return this.isExpoApp(true, true)
94cd5149Artem Egorov8 years ago52.then(isExpo => {
0a68f8dbArtem Egorov8 years ago53this.logger.logStream(".\n");
94cd5149Artem Egorov8 years ago54return this.patchAppJson(isExpo);
55});
d1d77244Jimmy Thomson9 years ago56}
57
58/**
59* Returns the current user. If there is none, asks user for username and password and logins to exponent servers.
60*/
5c8365a6Artem Egorov8 years ago61public loginToExponent(
62promptForInformation: (message: string, password: boolean) => Q.Promise<string>,
63showMessage: (message: string) => Q.Promise<string>
64): Q.Promise<XDL.IUser> {
d1d77244Jimmy Thomson9 years ago65this.lazilyInitialize();
66return XDL.currentUser()
67.then((user) => {
68if (!user) {
69let username = "";
d7d405aeYuri Skorokhodov7 years ago70return showMessage(localize("YouNeedToLoginToExpo", "You need to login to Expo. Please provide your Expo account username and password in the input boxes after closing this window. If you don't have an account, please go to https://expo.io to create one."))
d1d77244Jimmy Thomson9 years ago71.then(() =>
d7d405aeYuri Skorokhodov7 years ago72promptForInformation(localize("ExpoUsername", "Expo username"), false)
5c8365a6Artem Egorov8 years ago73).then((name: string) => {
d1d77244Jimmy Thomson9 years ago74username = name;
d7d405aeYuri Skorokhodov7 years ago75return promptForInformation(localize("ExpoPassword", "Expo password"), true);
d1d77244Jimmy Thomson9 years ago76})
5c8365a6Artem Egorov8 years ago77.then((password: string) =>
d1d77244Jimmy Thomson9 years ago78XDL.login(username, password));
79}
80return user;
81})
82.catch(error => {
83return Q.reject<XDL.IUser>(error);
84});
85}
86
94cd5149Artem Egorov8 years ago87public getExpPackagerOptions(): Q.Promise<ExpConfigPackager> {
6458f408Nikita Matrosov9 years ago88this.lazilyInitialize();
94cd5149Artem Egorov8 years ago89return this.getFromExpConfig("packagerOpts")
90.then(opts => opts || {});
6458f408Nikita Matrosov9 years ago91}
92
2956dba4Yuri Skorokhodov7 years ago93public isExpoApp(showProgress: boolean = false, logIfExpoIsNotInstalled: boolean = false): Q.Promise<boolean> {
db6fd42aRuslan Bikkinin7 years ago94if (showProgress) {
95this.logger.logStream("...");
96}
97
98const packageJsonPath = this.pathToFileInWorkspace("package.json");
99return this.fs.readFile(packageJsonPath)
100.then(content => {
101const packageJson = JSON.parse(content);
2956dba4Yuri Skorokhodov7 years ago102let isExp = (packageJson.dependencies && packageJson.dependencies.expo) || (packageJson.devDependencies && packageJson.devDependencies.expo);
103isExp = !!isExp;
db6fd42aRuslan Bikkinin7 years ago104if (showProgress) this.logger.logStream(".");
2956dba4Yuri Skorokhodov7 years ago105if (!isExp && logIfExpoIsNotInstalled) {
106// Expo requires expo package to be installed inside RN application in order to be able to run it
107// https://github.com/expo/expo-cli/issues/255#issuecomment-453214632
108this.logger.logStream("\n");
109this.logger.logStream(localize("ExpoPackageIsNotInstalled", "[Warning] expo package is not installed locally for your project, further errors may occur. Please, run \"npm install expo --save-dev\" inside your project."));
110this.logger.logStream("\n");
111}
db6fd42aRuslan Bikkinin7 years ago112return isExp;
113}).catch(() => {
114if (showProgress) {
115this.logger.logStream(".");
116}
117// Not in a react-native project
118return false;
119});
120}
121
1c32fe84Patricio Beltran9 years ago122/**
94cd5149Artem Egorov8 years ago123* Path to a given file inside the .vscode directory
1c32fe84Patricio Beltran9 years ago124*/
94cd5149Artem Egorov8 years ago125private dotvscodePath(filename: string): string {
126return path.join(this.workspaceRootPath, ".vscode", filename);
127}
128
129private createExpoEntry(name: string): Q.Promise<void> {
b0af599cJimmy Thomson9 years ago130this.lazilyInitialize();
94cd5149Artem Egorov8 years ago131return this.detectEntry()
132.then((entryPoint: string) => {
133const content = this.generateFileContent(name, entryPoint);
134return this.fs.writeFile(this.dotvscodePath(EXPONENT_INDEX), content);
135});
1c32fe84Patricio Beltran9 years ago136
94cd5149Artem Egorov8 years ago137}
1c32fe84Patricio Beltran9 years ago138
94cd5149Artem Egorov8 years ago139private detectEntry(): Q.Promise<string> {
140this.lazilyInitialize();
141return Q.all([
142this.fs.exists(this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX)),
143this.fs.exists(this.pathToFileInWorkspace(DEFAULT_IOS_INDEX)),
144this.fs.exists(this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX)),
145])
2956dba4Yuri Skorokhodov7 years ago146.spread((expo: boolean, ios: boolean): string => {
147return expo ? this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX) :
148ios ? this.pathToFileInWorkspace(DEFAULT_IOS_INDEX) :
149this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX);
150});
94cd5149Artem Egorov8 years ago151}
1c32fe84Patricio Beltran9 years ago152
94cd5149Artem Egorov8 years ago153private generateFileContent(name: string, entryPoint: string): string {
154return `// This file is automatically generated by VS Code
155// Please do not modify it manually. All changes will be lost.
156var React = require('${this.pathToFileInWorkspace("/node_modules/react")}');
157var { Component } = React;
158var ReactNative = require('${this.pathToFileInWorkspace("/node_modules/react-native")}');
159var { AppRegistry } = ReactNative;
160var entryPoint = require('${entryPoint}');
f6b41bbfAlexander Sorokin9 years ago161AppRegistry.registerRunnable('main', function(appParameters) {
94cd5149Artem Egorov8 years ago162AppRegistry.runApplication('${name}', appParameters);
1ae29ddcJimmy Thomson9 years ago163});`;
1c32fe84Patricio Beltran9 years ago164}
165
94cd5149Artem Egorov8 years ago166private patchAppJson(isExpo: boolean = true): Q.Promise<void> {
167return this.readAppJson()
168.catch(() => {
169// if app.json doesn't exist but it's ok, we will create it
170return {};
171})
172.then((config: AppJson) => {
173let expoConfig = <ExpConfig>(config.expo || {});
174if (!expoConfig.name || !expoConfig.slug) {
b0af599cJimmy Thomson9 years ago175return this.getPackageName()
94cd5149Artem Egorov8 years ago176.then((name: string) => {
177expoConfig.slug = expoConfig.slug || config.name || name.replace(" ", "-");
178expoConfig.name = expoConfig.name || config.name || name;
179config.expo = expoConfig;
180return config;
b0af599cJimmy Thomson9 years ago181});
182}
183
94cd5149Artem Egorov8 years ago184return config;
185})
186.then((config: AppJson) => {
187if (!config.expo.sdkVersion) {
188return this.exponentSdk(true)
189.then(sdkVersion => {
190config.expo.sdkVersion = sdkVersion;
191return config;
192});
1c32fe84Patricio Beltran9 years ago193}
94cd5149Artem Egorov8 years ago194return config;
1c32fe84Patricio Beltran9 years ago195})
94cd5149Artem Egorov8 years ago196.then((config: AppJson) => {
197if (!isExpo) {
198config.expo.entryPoint = this.dotvscodePath(EXPONENT_INDEX);
199}
1c32fe84Patricio Beltran9 years ago200
94cd5149Artem Egorov8 years ago201return config;
202})
203.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago204return config ? this.writeAppJson(config) : config;
1c32fe84Patricio Beltran9 years ago205})
94cd5149Artem Egorov8 years ago206.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago207return isExpo ? Q.resolve(void 0) : this.createExpoEntry(config.expo.name);
1c32fe84Patricio Beltran9 years ago208});
27710197Vladimir Kotikov8 years ago209}
1c32fe84Patricio Beltran9 years ago210
211/**
212* Exponent sdk version that maps to the current react-native version
213* If react native version is not supported it returns null.
214*/
831f4a85Patricio Beltran9 years ago215private exponentSdk(showProgress: boolean = false): Q.Promise<string> {
94cd5149Artem Egorov8 years ago216if (showProgress) {
0a68f8dbArtem Egorov8 years ago217this.logger.logStream("...");
1c32fe84Patricio Beltran9 years ago218}
94cd5149Artem Egorov8 years ago219
2e432a9eArtem Egorov8 years ago220return ReactNativeProjectHelper.getReactNativeVersion(this.projectRootPath)
94cd5149Artem Egorov8 years ago221.then(version => {
0a68f8dbArtem Egorov8 years ago222if (showProgress) this.logger.logStream(".");
94cd5149Artem Egorov8 years ago223return XDL.mapVersion(version)
224.then(sdkVersion => {
225if (!sdkVersion) {
226return XDL.supportedVersions()
227.then((versions) => {
d7d405aeYuri Skorokhodov7 years ago228return Q.reject<string>(ErrorHelper.getInternalError(InternalErrorCode.RNVersionNotSupportedByExponent, versions.join(", ")));
94cd5149Artem Egorov8 years ago229});
230}
231return sdkVersion;
1c32fe84Patricio Beltran9 years ago232});
233});
234}
235
94cd5149Artem Egorov8 years ago236
1c32fe84Patricio Beltran9 years ago237/**
94cd5149Artem Egorov8 years ago238* Name specified on user's package.json
1c32fe84Patricio Beltran9 years ago239*/
94cd5149Artem Egorov8 years ago240private getPackageName(): Q.Promise<string> {
241return new Package(this.projectRootPath, { fileSystem: this.fs }).name();
242}
243
244private getExpConfig(): Q.Promise<ExpConfig> {
245return this.readExpJson()
246.catch(err => {
247if (err.code === "ENOENT") {
248return this.readAppJson()
249.then((config: AppJson) => {
250return config.expo || {};
251});
1c32fe84Patricio Beltran9 years ago252}
94cd5149Artem Egorov8 years ago253
254return err;
1c32fe84Patricio Beltran9 years ago255});
256}
257
94cd5149Artem Egorov8 years ago258private getFromExpConfig(key: string): Q.Promise<any> {
259return this.getExpConfig()
260.then((config: ExpConfig) => config[key]);
1c32fe84Patricio Beltran9 years ago261}
262
263/**
94cd5149Artem Egorov8 years ago264* Returns the specified setting from exp.json if it exists
1c32fe84Patricio Beltran9 years ago265*/
94cd5149Artem Egorov8 years ago266private readExpJson(): Q.Promise<ExpConfig> {
267const expJsonPath = this.pathToFileInWorkspace(EXP_JSON);
268return this.fs.readFile(expJsonPath)
269.then(content => {
270return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago271});
272}
273
94cd5149Artem Egorov8 years ago274private readAppJson(): Q.Promise<AppJson> {
275const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
276return this.fs.readFile(appJsonPath)
277.then(content => {
278return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago279});
280}
281
94cd5149Artem Egorov8 years ago282private writeAppJson(config: AppJson): Q.Promise<AppJson> {
283const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
284return this.fs.writeFile(appJsonPath, JSON.stringify(config, null, 2))
285.then(() => config);
1c32fe84Patricio Beltran9 years ago286}
287
288/**
38edb09eAlexander Sorokin9 years ago289* Path to a given file from the workspace root
1c32fe84Patricio Beltran9 years ago290*/
291private pathToFileInWorkspace(filename: string): string {
94cd5149Artem Egorov8 years ago292return path.join(this.projectRootPath, filename).replace(DBL_SLASHES, "/");
1c32fe84Patricio Beltran9 years ago293}
294
a57e740bPatricio Beltran9 years ago295/**
296* Works as a constructor but only initiliazes when it's actually needed.
297*/
b0af599cJimmy Thomson9 years ago298private lazilyInitialize(): void {
a57e740bPatricio Beltran9 years ago299if (!this.hasInitialized) {
300this.hasInitialized = true;
301
302XDL.configReactNativeVersionWargnings();
38edb09eAlexander Sorokin9 years ago303XDL.attachLoggerStream(this.projectRootPath, {
a57e740bPatricio Beltran9 years ago304stream: {
305write: (chunk: any) => {
306if (chunk.level <= 30) {
0a68f8dbArtem Egorov8 years ago307this.logger.logStream(chunk.msg);
a57e740bPatricio Beltran9 years ago308} else if (chunk.level === 40) {
0a68f8dbArtem Egorov8 years ago309this.logger.warning(chunk.msg);
a57e740bPatricio Beltran9 years ago310} else {
0a68f8dbArtem Egorov8 years ago311this.logger.error(chunk.msg);
a57e740bPatricio Beltran9 years ago312}
313},
314},
315type: "raw",
316});
317}
318}
5c8365a6Artem Egorov8 years ago319}