microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.7.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/exponent/exponentHelper.ts

307lines · 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;
db6fd42aRuslan Bikkinin7 years ago28private fs: FileSystem = new 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...");
db6fd42aRuslan Bikkinin7 years ago45this.logger.logStream("Checking if this is Expo app.");
94cd5149Artem Egorov8 years ago46return this.isExpoApp(true)
47.then(isExpo => {
0a68f8dbArtem Egorov8 years ago48this.logger.logStream(".\n");
d1d77244Jimmy Thomson9 years ago49
94cd5149Artem Egorov8 years ago50return this.patchAppJson(isExpo);
51});
d1d77244Jimmy Thomson9 years ago52}
53
54/**
55* Returns the current user. If there is none, asks user for username and password and logins to exponent servers.
56*/
5c8365a6Artem Egorov8 years ago57public loginToExponent(
58promptForInformation: (message: string, password: boolean) => Q.Promise<string>,
59showMessage: (message: string) => Q.Promise<string>
60): Q.Promise<XDL.IUser> {
d1d77244Jimmy Thomson9 years ago61this.lazilyInitialize();
62return XDL.currentUser()
63.then((user) => {
64if (!user) {
65let username = "";
66return 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.")
67.then(() =>
68promptForInformation("Exponent username", false)
5c8365a6Artem Egorov8 years ago69).then((name: string) => {
d1d77244Jimmy Thomson9 years ago70username = name;
71return promptForInformation("Exponent password", true);
72})
5c8365a6Artem Egorov8 years ago73.then((password: string) =>
d1d77244Jimmy Thomson9 years ago74XDL.login(username, password));
75}
76return user;
77})
78.catch(error => {
79return Q.reject<XDL.IUser>(error);
80});
81}
82
94cd5149Artem Egorov8 years ago83public getExpPackagerOptions(): Q.Promise<ExpConfigPackager> {
6458f408Nikita Matrosov9 years ago84this.lazilyInitialize();
94cd5149Artem Egorov8 years ago85return this.getFromExpConfig("packagerOpts")
86.then(opts => opts || {});
6458f408Nikita Matrosov9 years ago87}
88
db6fd42aRuslan Bikkinin7 years ago89public isExpoApp(showProgress: boolean = false): Q.Promise<boolean> {
90if (showProgress) {
91this.logger.logStream("...");
92}
93
94const packageJsonPath = this.pathToFileInWorkspace("package.json");
95return this.fs.readFile(packageJsonPath)
96.then(content => {
97const packageJson = JSON.parse(content);
98const isExp = packageJson.dependencies && !!packageJson.dependencies.expo || false;
99if (showProgress) this.logger.logStream(".");
100return isExp;
101}).catch(() => {
102if (showProgress) {
103this.logger.logStream(".");
104}
105// Not in a react-native project
106return false;
107});
108}
109
1c32fe84Patricio Beltran9 years ago110/**
94cd5149Artem Egorov8 years ago111* Path to a given file inside the .vscode directory
1c32fe84Patricio Beltran9 years ago112*/
94cd5149Artem Egorov8 years ago113private dotvscodePath(filename: string): string {
114return path.join(this.workspaceRootPath, ".vscode", filename);
115}
116
117private createExpoEntry(name: string): Q.Promise<void> {
b0af599cJimmy Thomson9 years ago118this.lazilyInitialize();
94cd5149Artem Egorov8 years ago119return this.detectEntry()
120.then((entryPoint: string) => {
121const content = this.generateFileContent(name, entryPoint);
122return this.fs.writeFile(this.dotvscodePath(EXPONENT_INDEX), content);
123});
1c32fe84Patricio Beltran9 years ago124
94cd5149Artem Egorov8 years ago125}
1c32fe84Patricio Beltran9 years ago126
94cd5149Artem Egorov8 years ago127private detectEntry(): Q.Promise<string> {
128this.lazilyInitialize();
129return Q.all([
130this.fs.exists(this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX)),
131this.fs.exists(this.pathToFileInWorkspace(DEFAULT_IOS_INDEX)),
132this.fs.exists(this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX)),
133])
134.spread((expo: boolean, ios: boolean): string => {
135return expo ? this.pathToFileInWorkspace(DEFAULT_EXPONENT_INDEX) :
136ios ? this.pathToFileInWorkspace(DEFAULT_IOS_INDEX) :
137this.pathToFileInWorkspace(DEFAULT_ANDROID_INDEX);
138});
139}
1c32fe84Patricio Beltran9 years ago140
94cd5149Artem Egorov8 years ago141private generateFileContent(name: string, entryPoint: string): string {
142return `// This file is automatically generated by VS Code
143// Please do not modify it manually. All changes will be lost.
144var React = require('${this.pathToFileInWorkspace("/node_modules/react")}');
145var { Component } = React;
146var ReactNative = require('${this.pathToFileInWorkspace("/node_modules/react-native")}');
147var { AppRegistry } = ReactNative;
148var entryPoint = require('${entryPoint}');
f6b41bbfAlexander Sorokin9 years ago149AppRegistry.registerRunnable('main', function(appParameters) {
94cd5149Artem Egorov8 years ago150AppRegistry.runApplication('${name}', appParameters);
1ae29ddcJimmy Thomson9 years ago151});`;
1c32fe84Patricio Beltran9 years ago152}
153
94cd5149Artem Egorov8 years ago154private patchAppJson(isExpo: boolean = true): Q.Promise<void> {
155return this.readAppJson()
156.catch(() => {
157// if app.json doesn't exist but it's ok, we will create it
158return {};
159})
160.then((config: AppJson) => {
161let expoConfig = <ExpConfig>(config.expo || {});
162if (!expoConfig.name || !expoConfig.slug) {
b0af599cJimmy Thomson9 years ago163return this.getPackageName()
94cd5149Artem Egorov8 years ago164.then((name: string) => {
165expoConfig.slug = expoConfig.slug || config.name || name.replace(" ", "-");
166expoConfig.name = expoConfig.name || config.name || name;
167config.expo = expoConfig;
168return config;
b0af599cJimmy Thomson9 years ago169});
170}
171
94cd5149Artem Egorov8 years ago172return config;
173})
174.then((config: AppJson) => {
175if (!config.expo.sdkVersion) {
176return this.exponentSdk(true)
177.then(sdkVersion => {
178config.expo.sdkVersion = sdkVersion;
179return config;
180});
1c32fe84Patricio Beltran9 years ago181}
94cd5149Artem Egorov8 years ago182return config;
1c32fe84Patricio Beltran9 years ago183})
94cd5149Artem Egorov8 years ago184.then((config: AppJson) => {
185if (!isExpo) {
186config.expo.entryPoint = this.dotvscodePath(EXPONENT_INDEX);
187}
1c32fe84Patricio Beltran9 years ago188
94cd5149Artem Egorov8 years ago189return config;
190})
191.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago192return config ? this.writeAppJson(config) : config;
1c32fe84Patricio Beltran9 years ago193})
94cd5149Artem Egorov8 years ago194.then((config: AppJson) => {
5c8365a6Artem Egorov8 years ago195return isExpo ? Q.resolve(void 0) : this.createExpoEntry(config.expo.name);
1c32fe84Patricio Beltran9 years ago196});
27710197Vladimir Kotikov8 years ago197}
1c32fe84Patricio Beltran9 years ago198
199/**
200* Exponent sdk version that maps to the current react-native version
201* If react native version is not supported it returns null.
202*/
831f4a85Patricio Beltran9 years ago203private exponentSdk(showProgress: boolean = false): Q.Promise<string> {
94cd5149Artem Egorov8 years ago204if (showProgress) {
0a68f8dbArtem Egorov8 years ago205this.logger.logStream("...");
1c32fe84Patricio Beltran9 years ago206}
94cd5149Artem Egorov8 years ago207
2e432a9eArtem Egorov8 years ago208return ReactNativeProjectHelper.getReactNativeVersion(this.projectRootPath)
94cd5149Artem Egorov8 years ago209.then(version => {
0a68f8dbArtem Egorov8 years ago210if (showProgress) this.logger.logStream(".");
94cd5149Artem Egorov8 years ago211return XDL.mapVersion(version)
212.then(sdkVersion => {
213if (!sdkVersion) {
214return XDL.supportedVersions()
215.then((versions) => {
216return Q.reject<string>(new Error(`React Native version not supported by exponent. Major versions supported: ${versions.join(", ")}`));
217});
218}
219return sdkVersion;
1c32fe84Patricio Beltran9 years ago220});
221});
222}
223
94cd5149Artem Egorov8 years ago224
1c32fe84Patricio Beltran9 years ago225/**
94cd5149Artem Egorov8 years ago226* Name specified on user's package.json
1c32fe84Patricio Beltran9 years ago227*/
94cd5149Artem Egorov8 years ago228private getPackageName(): Q.Promise<string> {
229return new Package(this.projectRootPath, { fileSystem: this.fs }).name();
230}
231
232private getExpConfig(): Q.Promise<ExpConfig> {
233return this.readExpJson()
234.catch(err => {
235if (err.code === "ENOENT") {
236return this.readAppJson()
237.then((config: AppJson) => {
238return config.expo || {};
239});
1c32fe84Patricio Beltran9 years ago240}
94cd5149Artem Egorov8 years ago241
242return err;
1c32fe84Patricio Beltran9 years ago243});
244}
245
94cd5149Artem Egorov8 years ago246private getFromExpConfig(key: string): Q.Promise<any> {
247return this.getExpConfig()
248.then((config: ExpConfig) => config[key]);
1c32fe84Patricio Beltran9 years ago249}
250
251/**
94cd5149Artem Egorov8 years ago252* Returns the specified setting from exp.json if it exists
1c32fe84Patricio Beltran9 years ago253*/
94cd5149Artem Egorov8 years ago254private readExpJson(): Q.Promise<ExpConfig> {
255const expJsonPath = this.pathToFileInWorkspace(EXP_JSON);
256return this.fs.readFile(expJsonPath)
257.then(content => {
258return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago259});
260}
261
94cd5149Artem Egorov8 years ago262private readAppJson(): Q.Promise<AppJson> {
263const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
264return this.fs.readFile(appJsonPath)
265.then(content => {
266return JSON.parse(stripJSONComments(content));
1c32fe84Patricio Beltran9 years ago267});
268}
269
94cd5149Artem Egorov8 years ago270private writeAppJson(config: AppJson): Q.Promise<AppJson> {
271const appJsonPath = this.pathToFileInWorkspace(APP_JSON);
272return this.fs.writeFile(appJsonPath, JSON.stringify(config, null, 2))
273.then(() => config);
1c32fe84Patricio Beltran9 years ago274}
275
276/**
38edb09eAlexander Sorokin9 years ago277* Path to a given file from the workspace root
1c32fe84Patricio Beltran9 years ago278*/
279private pathToFileInWorkspace(filename: string): string {
94cd5149Artem Egorov8 years ago280return path.join(this.projectRootPath, filename).replace(DBL_SLASHES, "/");
1c32fe84Patricio Beltran9 years ago281}
282
a57e740bPatricio Beltran9 years ago283/**
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;
289
290XDL.configReactNativeVersionWargnings();
38edb09eAlexander Sorokin9 years ago291XDL.attachLoggerStream(this.projectRootPath, {
a57e740bPatricio Beltran9 years ago292stream: {
293write: (chunk: any) => {
294if (chunk.level <= 30) {
0a68f8dbArtem Egorov8 years ago295this.logger.logStream(chunk.msg);
a57e740bPatricio Beltran9 years ago296} else if (chunk.level === 40) {
0a68f8dbArtem Egorov8 years ago297this.logger.warning(chunk.msg);
a57e740bPatricio Beltran9 years ago298} else {
0a68f8dbArtem Egorov8 years ago299this.logger.error(chunk.msg);
a57e740bPatricio Beltran9 years ago300}
301},
302},
303type: "raw",
304});
305}
306}
5c8365a6Artem Egorov8 years ago307}