Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local React Native Cli support #1093

Merged
merged 56 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
48ee1f8
add new getReactNativeVersion method
RedMickey Sep 2, 2019
381f59b
add local RN cli support for debugging
RedMickey Sep 2, 2019
e0fe4db
set local cli as default parameter
RedMickey Sep 2, 2019
a0ead38
Thow logger from rn-extension to commandExec
RedMickey Sep 2, 2019
022ada4
add a description for useGlobalReactNativeCLI parameter
RedMickey Sep 3, 2019
b700d39
Merge branch 'master' into local-react-native-cli
RedMickey Sep 3, 2019
cc8b72b
Fix code after merging
RedMickey Sep 3, 2019
a4bcfb3
Remove node launcher
RedMickey Sep 3, 2019
bed2436
Change getting version errors handler
RedMickey Sep 3, 2019
2b740aa
Fix code style
RedMickey Sep 3, 2019
3db6ba0
Add customReactNativeCLIPath parameter to debug options
RedMickey Sep 11, 2019
dd1ae57
Add reactNativeGlobalCommandName parameter to debug options
RedMickey Sep 12, 2019
a2465ec
Add test for commandExecutor getReactNativeVersion func
RedMickey Sep 13, 2019
512ea48
Move reactNativeGlobalCommandName from launch.json to settings.json
RedMickey Sep 13, 2019
d064be5
Remove redundant conditional statements
RedMickey Sep 17, 2019
87943d6
Merge branch 'master' into local-react-native-cli
RedMickey Sep 19, 2019
f997f25
Merge branch 'master' into local-react-native-cli
RedMickey Oct 1, 2019
dc1b5d3
Create global CLI smoke tests
RedMickey Oct 1, 2019
cba9ccf
Little fix
RedMickey Oct 1, 2019
6fc0456
Remove smoke tests for global RN CLI
RedMickey Oct 2, 2019
799818b
Add unit tests for CLI select function
RedMickey Oct 2, 2019
fc8bcfc
Little code style fix
RedMickey Oct 2, 2019
d23cbc3
Add React Native 0.61 project
RedMickey Oct 2, 2019
e60874e
Merge branch 'master' into local-react-native-cli
SounD120 Oct 2, 2019
99f89a6
Change telemetry event name and add unit tests for local/global cli
RedMickey Oct 3, 2019
bf86322
Merge branch 'local-react-native-cli' of https://github.com/RedMickey…
RedMickey Oct 3, 2019
d37e0fb
Remove sampleReactNative061Project folder
RedMickey Oct 3, 2019
607214f
Small fix
RedMickey Oct 3, 2019
318d2a4
Add function of getting React Native version from React Native package
RedMickey Oct 9, 2019
8880fb1
Add unit tests for React Native version getting function
RedMickey Oct 9, 2019
8d17667
Merge branch 'master' into local-react-native-cli
SounD120 Oct 9, 2019
2912636
Merge branch 'master' into local-react-native-cli
SounD120 Oct 11, 2019
8f21fec
Fix comments and change React Native version verification
RedMickey Oct 15, 2019
91f6d17
getReactNativeVersion function unit test fix
RedMickey Oct 15, 2019
709278e
Move getReactNativeVersion func to ReactNativeProjectHelper
RedMickey Oct 16, 2019
7bd7f51
Fix comments, remove redundant operations
RedMickey Oct 17, 2019
831c97e
Remove redundant unit test
RedMickey Oct 17, 2019
04b53a6
Minor code fix
RedMickey Oct 17, 2019
19b4f7a
Merge branch 'master' into local-react-native-cli
SounD120 Oct 17, 2019
03e0159
Move noReactNativePackageError wrapper and roll back publishToExpHost…
RedMickey Oct 17, 2019
0886a40
Merge branch 'local-react-native-cli' of https://github.com/RedMickey…
RedMickey Oct 17, 2019
31f3028
Refactoring getting React Native version from node_modules
RedMickey Oct 21, 2019
f110e6d
Add unit tests for ReactNativeProjectHelper
RedMickey Oct 21, 2019
9ce5cd2
Roll back several previous changes and adapt unit tests
RedMickey Oct 21, 2019
affad3b
Merge branch 'master' into local-react-native-cli
SounD120 Oct 21, 2019
6a3901f
Add more unit tests for ReactNativeProjectHelper
RedMickey Oct 21, 2019
4edb3ed
Merge branch 'local-react-native-cli' of https://github.com/RedMickey…
RedMickey Oct 21, 2019
e622a5c
Change hash generation
RedMickey Oct 21, 2019
727168d
Minor fix
RedMickey Oct 21, 2019
99a5aff
Fix review comments
RedMickey Oct 21, 2019
3b25d44
Merge branch 'master' into local-react-native-cli
SounD120 Oct 22, 2019
2ef6bd8
Minor test description fix
RedMickey Oct 22, 2019
515804f
Merge branch 'local-react-native-cli' of https://github.com/RedMickey…
RedMickey Oct 22, 2019
17493e9
Merge branch 'master' into local-react-native-cli
SounD120 Oct 24, 2019
b2c22ce
Minor fix
RedMickey Oct 25, 2019
0166d70
Merge branch 'local-react-native-cli' of https://github.com/RedMickey…
RedMickey Oct 25, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,11 @@
"default": 8081,
"scope": "resource"
},
"react-native-tools.reactNativeGlobalCommandName": {
"description": "%reactNative.configuration.properties.react-native-tools.reactNativeGlobalCommandName.description%",
"type": "string",
"scope": "resource"
},
"react-native-tools.projectRoot": {
"description": "%reactNative.configuration.properties.react-native-tools.projectRoot.description%",
"type": "string",
Expand Down
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@
"reactNative.configuration.properties.react-native.android.envFile.device.description":"Absolute path to a file containing environment variable definitions.",
"reactNative.configuration.properties.react-native.packager.port.description":"React-native packager port",
"reactNative.configuration.properties.react-native-tools.projectRoot.description":"Subfolder in which the react-native project is located",
"reactNative.configuration.properties.react-native-tools.logLevel.description":"Logging level in extension"
"reactNative.configuration.properties.react-native-tools.logLevel.description":"Logging level in extension",
"reactNative.configuration.properties.react-native-tools.reactNativeGlobalCommandName.description": "The command name to be used to execute React Native CLI commands."
}
36 changes: 10 additions & 26 deletions src/common/commandExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.

import * as Q from "q";
import * as path from "path";
import {ChildProcess} from "child_process";
import {ILogger} from "../extension/log/LogHelper";
import { NullLogger } from "../extension/log/NullLogger";
import {NullLogger} from "../extension/log/NullLogger";
SounD120 marked this conversation as resolved.
Show resolved Hide resolved
import {ReactNativeProjectHelper} from "../common/reactNativeProjectHelper";
import {Node} from "./node/node";
import {ISpawnResult} from "./node/childProcess";
import {HostPlatform, HostPlatformId} from "./hostPlatform";
Expand Down Expand Up @@ -36,8 +38,7 @@ export enum CommandStatus {

export class CommandExecutor {

private static ReactNativeCommand = "react-native";
private static ReactNativeVersionCommand = "--version";
public static ReactNativeCommand: string | null;
dhanvikapila marked this conversation as resolved.
Show resolved Hide resolved
private childProcess = new Node.ChildProcess();

constructor(
Expand Down Expand Up @@ -74,29 +75,8 @@ export class CommandExecutor {
return this.spawnReactCommand("start", args, options);
}

/**
* Uses the `react-native --version` command to get the version used on the project.
* Returns null if the workspace is not a react native project
*/
public getReactNativeVersion(): Q.Promise<string> {
let deferred = Q.defer<string>();
const reactCommand = HostPlatform.getNpmCliCommand(CommandExecutor.ReactNativeCommand);
let output = "";

const result = this.childProcess.spawn(reactCommand,
[CommandExecutor.ReactNativeVersionCommand],
{ cwd: this.currentWorkingDirectory });

result.stdout.on("data", (data: Buffer) => {
output += data.toString();
});

result.stdout.on("end", () => {
const match = output.match(/react-native: ([\d\.]+)/);
deferred.resolve(match && match[1] || "");
});

return deferred.promise;
return ReactNativeProjectHelper.getReactNativeVersion(this.currentWorkingDirectory);
}

/**
Expand Down Expand Up @@ -125,7 +105,7 @@ export class CommandExecutor {
* Executes a react native command and waits for its completion.
*/
public spawnReactCommand(command: string, args: string[] = [], options: Options = {}): ISpawnResult {
const reactCommand = HostPlatform.getNpmCliCommand(CommandExecutor.ReactNativeCommand);
const reactCommand = HostPlatform.getNpmCliCommand(this.selectReactNativeCLI());
return this.spawnChildProcess(reactCommand, [command, ...args], options);
}

Expand Down Expand Up @@ -188,6 +168,10 @@ export class CommandExecutor {
return deferred.promise;
}

public selectReactNativeCLI(): string {
return CommandExecutor.ReactNativeCommand || path.resolve(this.currentWorkingDirectory, "node_modules", ".bin", "react-native");
}

private spawnChildProcess(command: string, args: string[], options: Options = {}): ISpawnResult {
const spawnOptions = Object.assign({}, { cwd: this.currentWorkingDirectory }, options);
const commandWithArgs = command + " " + args.join(" ");
Expand Down
1 change: 1 addition & 0 deletions src/common/error/errorStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const ERROR_STRINGS = {
[InternalErrorCode.RNTempFolderDeletionFailed]: localize("RNTempFolderDeletionFailed", "Couldn't delete the temporary folder {0}"),
[InternalErrorCode.CouldNotFindLocationOfNodeDebugger]: localize("CouldNotFindLocationOfNodeDebugger", "Couldn't find the location of the node-debugger extension"),
[InternalErrorCode.CouldNotFindWorkspace]: localize("CouldNotFindWorkspace", "Couldn't find any workspace or React Native project folder"),
[InternalErrorCode.ReactNativePackageIsNotInstalled]: localize("ReactNativePackageIsNotInstalled", "Couldn't find react-native package in node_modules. Please, run \"npm install\" inside your project to install it."),
[InternalErrorCode.PackagerRunningInDifferentPort]: localize("PackagerRunningInDifferentPort", "A packager cannot be started on port {0} because a packager process is already running on port {1}"),
[InternalErrorCode.ErrorWhileProcessingMessageInIPMSServer]: localize("ErrorWhileProcessingMessageInIPMSServer", "An error ocurred while handling message: {0}"),
[InternalErrorCode.ErrorNoPipeFound]: localize("ErrorNoPipeFound", "Unable to set up communication with VSCode react-native extension. Is this a react-native project, and have you made sure that the react-native npm package is installed at the root?"),
Expand Down
1 change: 1 addition & 0 deletions src/common/error/internalErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export enum InternalErrorCode {
ProjectVersionNotReadable = 603,
NotInReactNativeFolderError = 604,
CouldNotFindProjectVersion = 605,
ReactNativePackageIsNotInstalled = 606,

// Miscellaneous errors
TelemetryInitializationFailed = 701,
Expand Down
4 changes: 4 additions & 0 deletions src/common/node/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export class Package {
return this.parseProperty("dependencies");
}

public devDependencies(): Q.Promise<IPackageDependencyDict> {
return this.parseProperty("devDependencies");
}

public version(): Q.Promise<string> {
return this.parseProperty("version").then(version =>
typeof version === "string"
Expand Down
38 changes: 35 additions & 3 deletions src/common/reactNativeProjectHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import * as Q from "q";
import * as fs from "fs";
import * as path from "path";
import {CommandExecutor} from "./commandExecutor";
import {Package} from "./node/package";
import {ErrorHelper} from "../common/error/errorHelper";
import {InternalErrorCode} from "../common/error/internalErrorCode";

export class ReactNativeProjectHelper {

Expand All @@ -13,8 +15,38 @@ export class ReactNativeProjectHelper {
return ["0.54.0", "0.54.1", "0.54.2", "0.54.3", "0.54.4"];
}

public static getReactNativeVersion(projectRoot: string) {
return new CommandExecutor(projectRoot).getReactNativeVersion();
public static getReactNativeVersion(projectRoot: string): Q.Promise<string> {
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(projectRoot)
.catch(err => {
return ReactNativeProjectHelper.getReactNativeVersionFromProjectPackage(projectRoot);
dhanvikapila marked this conversation as resolved.
Show resolved Hide resolved
});
}

public static getReactNativePackageVersionFromNodeModules(projectRoot: string): Q.Promise<string> {
return new Package(projectRoot).dependencyPackage("react-native").version()
.catch(err => {
throw ErrorHelper.getInternalError(InternalErrorCode.ReactNativePackageIsNotInstalled);
});
}

public static getReactNativeVersionFromProjectPackage(cwd: string): Q.Promise<string> {
const rootProjectPackageJson = new Package(cwd);
return rootProjectPackageJson.dependencies()
.then(dependencies => {
if (dependencies["react-native"]) {
return dependencies["react-native"];
}
return rootProjectPackageJson.devDependencies()
.then(devDependencies => {
if (devDependencies["react-native"]) {
return devDependencies["react-native"];
}
return "";
});
})
.catch(err => {
return "";
});
}

/**
Expand Down
117 changes: 68 additions & 49 deletions src/extension/commandPaletteHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import {IAndroidRunOptions, IIOSRunOptions} from "./launchArgs";
import {ExponentPlatform} from "./exponent/exponentPlatform";
import {spawn, ChildProcess} from "child_process";
import {HostPlatform} from "../common/hostPlatform";
import {CommandExecutor} from "../common/commandExecutor";
import * as nls from "vscode-nls";
import { ErrorHelper } from "../common/error/errorHelper";
import { InternalErrorCode } from "../common/error/internalErrorCode";
import {ErrorHelper} from "../common/error/errorHelper";
import {InternalErrorCode} from "../common/error/internalErrorCode";
const localize = nls.loadMessageBundle();

interface IReactNativeStuff {
Expand Down Expand Up @@ -63,13 +64,16 @@ export class CommandPaletteHandler {
public static startPackager(): Q.Promise<void> {
return this.selectProject()
.then((project: IReactNativeProject) => {
return this.executeCommandInContext("startPackager", project.workspaceFolder, () => {
return project.packager.isRunning()
.then((running) => {
return running ? project.packager.stop() : Q.resolve(void 0);
});
})
.then(() => project.packager.start());
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(project.workspaceFolder.uri.path)
.then(version => {
return this.executeCommandInContext("startPackager", project.workspaceFolder, () => {
return project.packager.isRunning()
.then((running) => {
return running ? project.packager.stop() : Q.resolve(void 0);
});
})
.then(() => project.packager.start());
});
});
}

Expand Down Expand Up @@ -100,8 +104,11 @@ export class CommandPaletteHandler {
public static restartPackager(): Q.Promise<void> {
return this.selectProject()
.then((project: IReactNativeProject) => {
return this.executeCommandInContext("restartPackager", project.workspaceFolder, () =>
this.runRestartPackagerCommandAndUpdateStatus(project));
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(project.workspaceFolder.uri.path)
.then(version => {
return this.executeCommandInContext("restartPackager", project.workspaceFolder, () =>
this.runRestartPackagerCommandAndUpdateStatus(project));
});
});
}

Expand All @@ -128,19 +135,22 @@ export class CommandPaletteHandler {
return this.selectProject()
.then((project: IReactNativeProject) => {
TargetPlatformHelper.checkTargetPlatformSupport("android");
return this.executeCommandInContext("runAndroid", project.workspaceFolder, () => {
const platform = <AndroidPlatform>this.createPlatform(project, "android", AndroidPlatform, target);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
return platform.runApp(/*shouldLaunchInAllDevices*/true);
})
.then(() => {
return platform.disableJSDebuggingMode();
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(project.workspaceFolder.uri.path)
.then(version => {
return this.executeCommandInContext("runAndroid", project.workspaceFolder, () => {
const platform = <AndroidPlatform>this.createPlatform(project, "android", AndroidPlatform, target);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
return platform.runApp(/*shouldLaunchInAllDevices*/true);
})
.then(() => {
return platform.disableJSDebuggingMode();
});
});
});
});
});
}

Expand All @@ -150,21 +160,24 @@ export class CommandPaletteHandler {
public static runIos(target: TargetType = "simulator"): Q.Promise<void> {
return this.selectProject()
.then((project: IReactNativeProject) => {
TargetPlatformHelper.checkTargetPlatformSupport("ios");
return this.executeCommandInContext("runIos", project.workspaceFolder, () => {
const platform = <IOSPlatform>this.createPlatform(project, "ios", IOSPlatform, target);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
// Set the Debugging setting to disabled, because in iOS it's persisted across runs of the app
return platform.disableJSDebuggingMode();
})
.catch(() => { }) // If setting the debugging mode fails, we ignore the error and we run the run ios command anyways
.then(() => {
return platform.runApp();
});
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(project.workspaceFolder.uri.path)
.then(version => {
TargetPlatformHelper.checkTargetPlatformSupport("ios");
return this.executeCommandInContext("runIos", project.workspaceFolder, () => {
const platform = <IOSPlatform>this.createPlatform(project, "ios", IOSPlatform, target);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
// Set the Debugging setting to disabled, because in iOS it's persisted across runs of the app
return platform.disableJSDebuggingMode();
})
.catch(() => { }) // If setting the debugging mode fails, we ignore the error and we run the run ios command anyways
.then(() => {
return platform.runApp();
});
});
});
});
}
Expand All @@ -175,18 +188,21 @@ export class CommandPaletteHandler {
public static runExponent(): Q.Promise<void> {
return this.selectProject()
.then((project: IReactNativeProject) => {
return this.loginToExponent(project)
.then(() => {
return this.executeCommandInContext("runExponent", project.workspaceFolder, () => {
const platform = <ExponentPlatform>this.createPlatform(project, "exponent", ExponentPlatform);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
return platform.runApp();
return ReactNativeProjectHelper.getReactNativePackageVersionFromNodeModules(project.workspaceFolder.uri.path)
.then(version => {
return this.loginToExponent(project)
.then(() => {
return this.executeCommandInContext("runExponent", project.workspaceFolder, () => {
const platform = <ExponentPlatform>this.createPlatform(project, "exponent", ExponentPlatform);
return platform.beforeStartPackager()
.then(() => {
return platform.startPackager();
})
.then(() => {
return platform.runApp();
});
});
});
});
SounD120 marked this conversation as resolved.
Show resolved Hide resolved
});
});
}
Expand Down Expand Up @@ -394,6 +410,7 @@ export class CommandPaletteHandler {
const envArgs = SettingsHelper.getEnvArgs(platform, target, project.workspaceFolder.uri);
const envFile = SettingsHelper.getEnvFile(platform, target, project.workspaceFolder.uri);
const projectRoot = SettingsHelper.getReactNativeProjectRoot(project.workspaceFolder.uri.fsPath);
const reactNativeGlobalCommandName = SettingsHelper.getReactNativeGlobalCommandName(project.workspaceFolder.uri);
const runOptions: IAndroidRunOptions | IIOSRunOptions = {
platform: platform,
workspaceRoot: project.workspaceFolder.uri.fsPath,
Expand All @@ -404,6 +421,8 @@ export class CommandPaletteHandler {
envFile: envFile,
};

CommandExecutor.ReactNativeCommand = reactNativeGlobalCommandName;

return runOptions;
}
}
Loading