microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.6.5

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/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";
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");
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;
0a68f8dbArtem Egorov8 years ago30private logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
1c32fe84Patricio Beltran9 years ago31
38edb09eAlexander Sorokin9 years ago32public constructor(workspaceRootPath: string, projectRootPath: string) {
33this.workspaceRootPath = workspaceRootPath;
34this.projectRootPath = projectRootPath;
a57e740bPatricio Beltran9 years ago35this.hasInitialized = false;
36// Constructor is slim by design. This is to add as less computation as possible
37// to the initialization of the extension. If a public method is added, make sure
b0af599cJimmy Thomson9 years ago38// to call this.lazilyInitialize() at the begining of the code to be sure all variables
a57e740bPatricio Beltran9 years ago39// are correctly initialized.
1c32fe84Patricio Beltran9 years ago40}
41
d1d77244Jimmy Thomson9 years ago42public configureExponentEnvironment(): Q.Promise<void> {
43this.lazilyInitialize();
0a68f8dbArtem Egorov8 years ago44this.logger.info("Making sure your project uses the correct dependencies for exponent. This may take a while...");
94cd5149Artem Egorov8 years ago45return this.isExpoApp(true)
46.then(isExpo => {
0a68f8dbArtem Egorov8 years ago47this.logger.logStream(".\n");
d1d77244Jimmy Thomson9 years ago48
94cd5149Artem Egorov8 years ago49return this.patchAppJson(isExpo);
50});
d1d77244Jimmy Thomson9 years ago51}
52
53/**
54* Returns the current user. If there is none, asks user for username and password and logins to exponent servers.
55*/
5c8365a6Artem Egorov8 years ago56public loginToExponent(
57promptForInformation: (message: string, password: boolean) => Q.Promise<string>,
58showMessage: (message: string) => Q.Promise<string>
59): Q.Promise<XDL.IUser> {
d1d77244Jimmy Thomson9 years ago60this.lazilyInitialize();
61return XDL.currentUser()
62.then((user) => {
63if (!user) {
64let username = "";
65return 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.")
66.then(() =>
67promptForInformation("Exponent username", false)
5c8365a6Artem Egorov8 years ago68).then((name: string) => {
d1d77244Jimmy Thomson9 years ago69username = name;
70return promptForInformation("Exponent password", true);
71})
5c8365a6Artem Egorov8 years ago72.then((password: string) =>
d1d77244Jimmy Thomson9 years ago73XDL.login(username, password));
74}
75return user;
76})
77.catch(error => {
78return Q.reject<XDL.IUser>(error);
79});
80}
81
94cd5149Artem Egorov8 years ago82public getExpPackagerOptions(): Q.Promise<ExpConfigPackager> {
6458f408Nikita Matrosov9 years ago83this.lazilyInitialize();
94cd5149Artem Egorov8 years ago84return this.getFromExpConfig("packagerOpts")
85.then(opts => opts || {});
6458f408Nikita Matrosov9 years ago86}
87
1c32fe84Patricio Beltran9 years ago88/**
94cd5149Artem Egorov8 years ago89* Path to a given file inside the .vscode directory
1c32fe84Patricio Beltran9 years ago90*/
94cd5149Artem Egorov8 years ago91private dotvscodePath(filename: string): string {
92return path.join(this.workspaceRootPath, ".vscode", filename);
93}
94
95private createExpoEntry(name: string): Q.Promise<void> {
b0af599cJimmy Thomson9 years ago96this.lazilyInitialize();
94cd5149Artem Egorov8 years ago97return this.detectEntry()
98.then((entryPoint: string) => {
99const content = this.generateFileContent(name, entryPoint);
100return this.fs.writeFile(this.dotvscodePath(EXPONENT_INDEX), content);
101});
1c32fe84Patricio Beltran9 years ago102
94cd5149Artem Egorov8 years ago103}
1c32fe84Patricio Beltran9 years ago104
94cd5149Artem Egorov8 years ago105private detectEntry(): Q.Promise<string> {
106this.lazilyInitialize();
107return Q.all([
108this.fs.exists(this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX)),
109this.fs.exists(this.pathToFileInWorkspace(DEFAULT_IOS_INDEX)),
110this.fs.exists(this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX)),
111])
112.spread((expo: boolean, ios: boolean): string => {
113return expo ? this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX) :
114ios ? this.pathToFileInWorkspace(DEFAULT_IOS_INDEX) :
115this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX);
116});
117}
1c32fe84Patricio Beltran9 years ago118
94cd5149Artem Egorov8 years ago119private generateFileContent(name: string, entryPoint: string): string {
120return `// This file is automatically generated by VS Code
121// Please do not modify it manually. All changes will be lost.
122var React = require('${this.pathToFileInWorkspace("/node_modules/react")}');
123var { Component } = React;
124var ReactNative = require('${this.pathToFileInWorkspace("/node_modules/react-native")}');
125var { AppRegistry } = ReactNative;
126var entryPoint = require('${entryPoint}');
f6b41bbfAlexander Sorokin9 years ago127AppRegistry.registerRunnable('main', function(appParameters) {
94cd5149Artem Egorov8 years ago128AppRegistry.runApplication('${name}', appParameters);
1ae29ddcJimmy Thomson9 years ago129});`;
1c32fe84Patricio Beltran9 years ago130}
131
94cd5149Artem Egorov8 years ago132private patchAppJson(isExpo: boolean = true): Q.Promise<void> {
133return this.readAppJson()
134.catch(() => {
135// if app.json doesn't exist but it's ok, we will create it
136return {};
137})
138.then((config: AppJson) => {
139let expoConfig = <ExpConfig>(config.expo || {});
140if (!expoConfig.name || !expoConfig.slug) {
b0af599cJimmy Thomson9 years ago141return this.getPackageName()
94cd5149Artem Egorov8 years ago142.then((name: string) => {
143expoConfig.slug = expoConfig.slug || config.name || name.replace(" ", "-");
144expoConfig.name = expoConfig.name || config.name || name;
145config.expo = expoConfig;
146return config;
b0af599cJimmy Thomson9 years ago147});
148}
149
94cd5149Artem Egorov8 years ago150return config;
151})
152.then((config: AppJson) => {
153if (!config.expo.sdkVersion) {
154return this.exponentSdk(true)
155.then(sdkVersion => {
156config.expo.sdkVersion = sdkVersion;
157return config;
158});
1c32fe84Patricio Beltran9 years ago159}
94cd5149Artem Egorov8 years ago160return config;
1c32fe84Patricio Beltran9 years ago161})
94cd5149Artem Egorov8 years ago162.then((config: AppJson) => {
163if (!isExpo) {
164config.expo.entryPoint = this.dotvscodePath(EXPONENT_INDEX);
165}
1c32fe84Patricio Beltran9 years ago166
94cd5149Artem Egorov8 years ago167return config;
168})
169.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago170return config ? this.writeAppJson(config) : config;
1c32fe84Patricio Beltran9 years ago171})
94cd5149Artem Egorov8 years ago172.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago173return isExpo ? Q.resolve(void 0) : this.createExpoEntry(config.expo.name);
1c32fe84Patricio Beltran9 years ago174});
27710197Vladimir Kotikov8 years ago175}
1c32fe84Patricio Beltran9 years ago176
177/**
178* Exponent sdk version that maps to the current react-native version
179* If react native version is not supported it returns null.
180*/
831f4a85Patricio Beltran9 years ago181private exponentSdk(showProgress: boolean = false): Q.Promise<string> {
94cd5149Artem Egorov8 years ago182if (showProgress) {
0a68f8dbArtem Egorov8 years ago183this.logger.logStream("...");
1c32fe84Patricio Beltran9 years ago184}
94cd5149Artem Egorov8 years ago185
2e432a9eArtem Egorov8 years ago186return ReactNativeProjectHelper.getReactNativeVersion(this.projectRootPath)
94cd5149Artem Egorov8 years ago187.then(version => {
0a68f8dbArtem Egorov8 years ago188if (showProgress) this.logger.logStream(".");
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> {
0a68f8dbArtem Egorov8 years ago262this.logger.logStream("Checking if this is Expo app.");
94cd5149Artem Egorov8 years ago263if (showProgress) {
0a68f8dbArtem Egorov8 years ago264this.logger.logStream("...");
94cd5149Artem Egorov8 years ago265}
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;
0a68f8dbArtem Egorov8 years ago272if (showProgress) this.logger.logStream(".");
94cd5149Artem Egorov8 years ago273return isExp;
274}).catch(() => {
275if (showProgress) {
0a68f8dbArtem Egorov8 years ago276this.logger.logStream(".");
94cd5149Artem Egorov8 years ago277}
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) {
0a68f8dbArtem Egorov8 years ago296this.logger.logStream(chunk.msg);
a57e740bPatricio Beltran9 years ago297} else if (chunk.level === 40) {
0a68f8dbArtem Egorov8 years ago298this.logger.warning(chunk.msg);
a57e740bPatricio Beltran9 years ago299} else {
0a68f8dbArtem Egorov8 years ago300this.logger.error(chunk.msg);
a57e740bPatricio Beltran9 years ago301}
302},
303},
304type: "raw",
305});
306}
307}
5c8365a6Artem Egorov8 years ago308}