microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.4.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/exponent/exponentHelper.ts

308lines · 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";
94cd5149Artem Egorov8 years ago9import { Package } from "../node/package";
10import { ReactNativeProjectHelper } from "../reactNativeProjectHelper";
11import { FileSystem } from "../node/fileSystem";
12import { Log } from "../log/log";
13import stripJSONComments = require("strip-json-comments");
1c32fe84Patricio Beltran9 years ago14
94cd5149Artem Egorov8 years ago15const APP_JSON = "app.json";
16const EXP_JSON = "exp.json";
1c32fe84Patricio Beltran9 years ago17
18const EXPONENT_INDEX = "exponentIndex.js";
94cd5149Artem Egorov8 years ago19const DEFAULT_EXPONENT_INDEX = "index.js";
1c32fe84Patricio Beltran9 years ago20const DEFAULT_IOS_INDEX = "index.ios.js";
21const DEFAULT_ANDROID_INDEX = "index.android.js";
22
94cd5149Artem Egorov8 years ago23const DBL_SLASHES = /\\/g;
1c32fe84Patricio Beltran9 years ago24
25export class ExponentHelper {
38edb09eAlexander Sorokin9 years ago26private workspaceRootPath: string;
94cd5149Artem Egorov8 years ago27private projectRootPath: string;
28private fs: FileSystem;
a57e740bPatricio Beltran9 years ago29private hasInitialized: boolean;
1c32fe84Patricio Beltran9 years ago30
38edb09eAlexander Sorokin9 years ago31public constructor(workspaceRootPath: string, projectRootPath: string) {
32this.workspaceRootPath = workspaceRootPath;
33this.projectRootPath = projectRootPath;
a57e740bPatricio Beltran9 years ago34this.hasInitialized = false;
35// Constructor is slim by design. This is to add as less computation as possible
36// to the initialization of the extension. If a public method is added, make sure
b0af599cJimmy Thomson9 years ago37// to call this.lazilyInitialize() at the begining of the code to be sure all variables
a57e740bPatricio Beltran9 years ago38// are correctly initialized.
1c32fe84Patricio Beltran9 years ago39}
40
d1d77244Jimmy Thomson9 years ago41public configureExponentEnvironment(): Q.Promise<void> {
42this.lazilyInitialize();
43Log.logMessage("Making sure your project uses the correct dependencies for exponent. This may take a while...");
94cd5149Artem Egorov8 years ago44return this.isExpoApp(true)
45.then(isExpo => {
46Log.logString(".\n");
d1d77244Jimmy Thomson9 years ago47
94cd5149Artem Egorov8 years ago48return this.patchAppJson(isExpo);
49});
d1d77244Jimmy Thomson9 years ago50}
51
52/**
53* Returns the current user. If there is none, asks user for username and password and logins to exponent servers.
54*/
5c8365a6Artem Egorov8 years ago55public loginToExponent(
56promptForInformation: (message: string, password: boolean) => Q.Promise<string>,
57showMessage: (message: string) => Q.Promise<string>
58): Q.Promise<XDL.IUser> {
d1d77244Jimmy Thomson9 years ago59this.lazilyInitialize();
60return XDL.currentUser()
61.then((user) => {
62if (!user) {
63let username = "";
64return showMessage("You need to login to exponent. Please provide username and password to login. If you don't have an account we will create one for you.")
65.then(() =>
66promptForInformation("Exponent username", false)
5c8365a6Artem Egorov8 years ago67).then((name: string) => {
d1d77244Jimmy Thomson9 years ago68username = name;
69return promptForInformation("Exponent password", true);
70})
5c8365a6Artem Egorov8 years ago71.then((password: string) =>
d1d77244Jimmy Thomson9 years ago72XDL.login(username, password));
73}
74return user;
75})
76.catch(error => {
77return Q.reject<XDL.IUser>(error);
78});
79}
80
94cd5149Artem Egorov8 years ago81public getExpPackagerOptions(): Q.Promise<ExpConfigPackager> {
6458f408Nikita Matrosov9 years ago82this.lazilyInitialize();
94cd5149Artem Egorov8 years ago83return this.getFromExpConfig("packagerOpts")
84.then(opts => opts || {});
6458f408Nikita Matrosov9 years ago85}
86
1c32fe84Patricio Beltran9 years ago87/**
94cd5149Artem Egorov8 years ago88* Path to a given file inside the .vscode directory
1c32fe84Patricio Beltran9 years ago89*/
94cd5149Artem Egorov8 years ago90private dotvscodePath(filename: string): string {
91return path.join(this.workspaceRootPath, ".vscode", filename);
92}
93
94private createExpoEntry(name: string): Q.Promise<void> {
b0af599cJimmy Thomson9 years ago95this.lazilyInitialize();
94cd5149Artem Egorov8 years ago96return this.detectEntry()
97.then((entryPoint: string) => {
98const content = this.generateFileContent(name, entryPoint);
99return this.fs.writeFile(this.dotvscodePath(EXPONENT_INDEX), content);
100});
1c32fe84Patricio Beltran9 years ago101
94cd5149Artem Egorov8 years ago102}
1c32fe84Patricio Beltran9 years ago103
94cd5149Artem Egorov8 years ago104private detectEntry(): Q.Promise<string> {
105this.lazilyInitialize();
106return Q.all([
107this.fs.exists(this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX)),
108this.fs.exists(this.pathToFileInWorkspace(DEFAULT_IOS_INDEX)),
109this.fs.exists(this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX)),
110])
111.spread((expo: boolean, ios: boolean): string => {
112return expo ? this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX) :
113ios ? this.pathToFileInWorkspace(DEFAULT_IOS_INDEX) :
114this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX);
115});
116}
1c32fe84Patricio Beltran9 years ago117
94cd5149Artem Egorov8 years ago118private generateFileContent(name: string, entryPoint: string): string {
119return `// This file is automatically generated by VS Code
120// Please do not modify it manually. All changes will be lost.
121var React = require('${this.pathToFileInWorkspace("/node_modules/react")}');
122var { Component } = React;
123var ReactNative = require('${this.pathToFileInWorkspace("/node_modules/react-native")}');
124var { AppRegistry } = ReactNative;
125var entryPoint = require('${entryPoint}');
f6b41bbfAlexander Sorokin9 years ago126AppRegistry.registerRunnable('main', function(appParameters) {
94cd5149Artem Egorov8 years ago127AppRegistry.runApplication('${name}', appParameters);
1ae29ddcJimmy Thomson9 years ago128});`;
1c32fe84Patricio Beltran9 years ago129}
130
94cd5149Artem Egorov8 years ago131private patchAppJson(isExpo: boolean = true): Q.Promise<void> {
132return this.readAppJson()
133.catch(() => {
134// if app.json doesn't exist but it's ok, we will create it
135return {};
136})
137.then((config: AppJson) => {
138let expoConfig = <ExpConfig>(config.expo || {});
139if (!expoConfig.name || !expoConfig.slug) {
b0af599cJimmy Thomson9 years ago140return this.getPackageName()
94cd5149Artem Egorov8 years ago141.then((name: string) => {
142expoConfig.slug = expoConfig.slug || config.name || name.replace(" ", "-");
143expoConfig.name = expoConfig.name || config.name || name;
144config.expo = expoConfig;
145return config;
b0af599cJimmy Thomson9 years ago146});
147}
148
94cd5149Artem Egorov8 years ago149return config;
150})
151.then((config: AppJson) => {
152if (!config.expo.sdkVersion) {
153return this.exponentSdk(true)
154.then(sdkVersion => {
155config.expo.sdkVersion = sdkVersion;
156return config;
157});
1c32fe84Patricio Beltran9 years ago158}
94cd5149Artem Egorov8 years ago159return config;
1c32fe84Patricio Beltran9 years ago160})
94cd5149Artem Egorov8 years ago161.then((config: AppJson) => {
162if (!isExpo) {
163config.expo.entryPoint = this.dotvscodePath(EXPONENT_INDEX);
164}
1c32fe84Patricio Beltran9 years ago165
94cd5149Artem Egorov8 years ago166return config;
167})
168.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago169return config ? this.writeAppJson(config) : config;
1c32fe84Patricio Beltran9 years ago170})
94cd5149Artem Egorov8 years ago171.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago172return isExpo ? Q.resolve(void 0) : this.createExpoEntry(config.expo.name);
1c32fe84Patricio Beltran9 years ago173});
27710197Vladimir Kotikov8 years ago174}
1c32fe84Patricio Beltran9 years ago175
176/**
177* Exponent sdk version that maps to the current react-native version
178* If react native version is not supported it returns null.
179*/
831f4a85Patricio Beltran9 years ago180private exponentSdk(showProgress: boolean = false): Q.Promise<string> {
94cd5149Artem Egorov8 years ago181if (showProgress) {
182Log.logString("...");
1c32fe84Patricio Beltran9 years ago183}
94cd5149Artem Egorov8 years ago184
185let reactNativeProjectHelper = new ReactNativeProjectHelper(this.projectRootPath);
186return reactNativeProjectHelper.getReactNativeVersion()
187.then(version => {
831f4a85Patricio Beltran9 years ago188if (showProgress) Log.logString(".");
94cd5149Artem Egorov8 years ago189return XDL.mapVersion(version)
190.then(sdkVersion => {
191if (!sdkVersion) {
192return XDL.supportedVersions()
193.then((versions) => {
194return Q.reject<string>(new Error(`React Native version not supported by exponent. Major versions supported: ${versions.join(", ")}`));
195});
196}
197return sdkVersion;
1c32fe84Patricio Beltran9 years ago198});
199});
200}
201
94cd5149Artem Egorov8 years ago202
1c32fe84Patricio Beltran9 years ago203/**
94cd5149Artem Egorov8 years ago204* Name specified on user's package.json
1c32fe84Patricio Beltran9 years ago205*/
94cd5149Artem Egorov8 years ago206private getPackageName(): Q.Promise<string> {
207return new Package(this.projectRootPath, { fileSystem: this.fs }).name();
208}
209
210private getExpConfig(): Q.Promise<ExpConfig> {
211return this.readExpJson()
212.catch(err => {
213if (err.code === "ENOENT") {
214return this.readAppJson()
215.then((config: AppJson) => {
216return config.expo || {};
217});
1c32fe84Patricio Beltran9 years ago218}
94cd5149Artem Egorov8 years ago219
220return err;
1c32fe84Patricio Beltran9 years ago221});
222}
223
94cd5149Artem Egorov8 years ago224private getFromExpConfig(key: string): Q.Promise<any> {
225return this.getExpConfig()
226.then((config: ExpConfig) => config[key]);
1c32fe84Patricio Beltran9 years ago227}
228
229/**
94cd5149Artem Egorov8 years ago230* Returns the specified setting from exp.json if it exists
1c32fe84Patricio Beltran9 years ago231*/
94cd5149Artem Egorov8 years ago232private readExpJson(): Q.Promise<ExpConfig> {
233const expJsonPath = this.pathToFileInWorkspace(EXP_JSON);
234return this.fs.readFile(expJsonPath)
235.then(content => {
236return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago237});
238}
239
94cd5149Artem Egorov8 years ago240private readAppJson(): Q.Promise<AppJson> {
241const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
242return this.fs.readFile(appJsonPath)
243.then(content => {
244return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago245});
246}
247
94cd5149Artem Egorov8 years ago248private writeAppJson(config: AppJson): Q.Promise<AppJson> {
249const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
250return this.fs.writeFile(appJsonPath, JSON.stringify(config, null, 2))
251.then(() => config);
1c32fe84Patricio Beltran9 years ago252}
253
254/**
38edb09eAlexander Sorokin9 years ago255* Path to a given file from the workspace root
1c32fe84Patricio Beltran9 years ago256*/
257private pathToFileInWorkspace(filename: string): string {
94cd5149Artem Egorov8 years ago258return path.join(this.projectRootPath, filename).replace(DBL_SLASHES, "/");
1c32fe84Patricio Beltran9 years ago259}
260
94cd5149Artem Egorov8 years ago261private isExpoApp(showProgress: boolean = false): Q.Promise<boolean> {
262Log.logString("Checking if this is Expo app.");
263if (showProgress) {
264Log.logString("...");
265}
266
267const packageJsonPath = this.pathToFileInWorkspace("package.json");
268return this.fs.readFile(packageJsonPath)
269.then(content => {
270const packageJson = JSON.parse(content);
271const isExp = packageJson.dependencies && !!packageJson.dependencies.expo || false;
272if (showProgress) Log.logString(".");
273return isExp;
274}).catch(() => {
275if (showProgress) {
276Log.logString(".");
277}
278// Not in a react-native project
279return false;
280});
1c32fe84Patricio Beltran9 years ago281}
a57e740bPatricio Beltran9 years ago282
283/**
284* Works as a constructor but only initiliazes when it's actually needed.
285*/
b0af599cJimmy Thomson9 years ago286private lazilyInitialize(): void {
a57e740bPatricio Beltran9 years ago287if (!this.hasInitialized) {
288this.hasInitialized = true;
94cd5149Artem Egorov8 years ago289this.fs = new FileSystem();
a57e740bPatricio Beltran9 years ago290
291XDL.configReactNativeVersionWargnings();
38edb09eAlexander Sorokin9 years ago292XDL.attachLoggerStream(this.projectRootPath, {
a57e740bPatricio Beltran9 years ago293stream: {
294write: (chunk: any) => {
295if (chunk.level <= 30) {
296Log.logString(chunk.msg);
297} else if (chunk.level === 40) {
298Log.logWarning(chunk.msg);
299} else {
300Log.logError(chunk.msg);
301}
302},
303},
304type: "raw",
305});
306}
307}
5c8365a6Artem Egorov8 years ago308}