Skip to content

Commit 240e6c7

Browse files
authored
feat: interactive ns start command (#5757)
2 parents 2f58e3b + 27d3940 commit 240e6c7

37 files changed

+3896
-12347
lines changed

lib/bootstrap.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { injector } from "./common/yok";
22

33
require("./common/bootstrap");
4+
45
injector.requirePublicClass("logger", "./common/logger/logger");
56
injector.require("config", "./config");
67
injector.require("options", "./options");
@@ -278,6 +279,7 @@ injector.require(
278279
"./helpers/android-bundle-validator-helper"
279280
);
280281
injector.require("liveSyncCommandHelper", "./helpers/livesync-command-helper");
282+
281283
injector.require("deployCommandHelper", "./helpers/deploy-command-helper");
282284
injector.require("platformCommandHelper", "./helpers/platform-command-helper");
283285
injector.require("optionsTracker", "./helpers/options-track-helper");
@@ -442,3 +444,10 @@ injector.require(
442444
injector.require("tempService", "./services/temp-service");
443445

444446
injector.require("sharedEventBus", "./shared-event-bus");
447+
448+
injector.require("keyCommandHelper", "./helpers/key-command-helper");
449+
450+
injector.requireCommand("start", "./commands/start");
451+
injector.require("startService", "./services/start-service");
452+
453+
require("./key-commands/bootstrap");

lib/color.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
// using chalk as some of our other dependencies are already using it...
22
// exporting from here so we can easily refactor to a different color library if needed
3+
import * as ansi from "ansi-colors";
34
import * as chalk from "chalk";
45

56
export type Color = typeof chalk.Color;
67

8+
export function stripColors(formatStr: string) {
9+
return ansi.stripColor(formatStr);
10+
}
11+
712
export const color = chalk;

lib/commands/clean.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "../definitions/project";
1111

1212
import type { PromptObject } from "prompts";
13-
import { IOptions } from "../declarations";
13+
import { IOptions, IStaticConfig } from "../declarations";
1414
import {
1515
ITerminalSpinner,
1616
ITerminalSpinnerService,
@@ -22,8 +22,6 @@ import { resolve } from "path";
2222
import { readdir } from "fs/promises";
2323
import { isInteractive } from "../common/helpers";
2424

25-
const CLIPath = resolve(__dirname, "..", "..", "bin", "nativescript.js");
26-
2725
function bytesToHumanReadable(bytes: number): string {
2826
const units = ["B", "KB", "MB", "GB", "TB"];
2927
let unit = 0;
@@ -90,7 +88,8 @@ export class CleanCommand implements ICommand {
9088
private $prompter: IPrompter,
9189
private $logger: ILogger,
9290
private $options: IOptions,
93-
private $childProcess: IChildProcess
91+
private $childProcess: IChildProcess,
92+
private $staticConfig: IStaticConfig
9493
) {}
9594

9695
public async execute(args: string[]): Promise<void> {
@@ -198,9 +197,12 @@ export class CleanCommand implements ICommand {
198197
paths,
199198
(p) => {
200199
return this.$childProcess
201-
.exec(`node ${CLIPath} clean --dry-run --json --disable-analytics`, {
202-
cwd: p,
203-
})
200+
.exec(
201+
`node ${this.$staticConfig.cliBinPath} clean --dry-run --json --disable-analytics`,
202+
{
203+
cwd: p,
204+
}
205+
)
204206
.then((res) => {
205207
const paths: Record<string, number> = JSON.parse(res).stats;
206208
return Object.values(paths).reduce((a, b) => a + b, 0);
@@ -290,7 +292,7 @@ export class CleanCommand implements ICommand {
290292

291293
const ok = await this.$childProcess
292294
.exec(
293-
`node ${CLIPath} clean ${
295+
`node ${this.$staticConfig.cliBinPath} clean ${
294296
this.$options.dryRun ? "--dry-run" : ""
295297
} --json --disable-analytics`,
296298
{

lib/commands/run.ts

+25-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import { ERROR_NO_VALID_SUBCOMMAND_FORMAT } from "../common/constants";
2-
import {
3-
ANDROID_RELEASE_BUILD_ERROR_MESSAGE,
4-
ANDROID_APP_BUNDLE_SIGNING_ERROR_MESSAGE,
5-
} from "../constants";
2+
import { IErrors, IHostInfo } from "../common/declarations";
63
import { cache } from "../common/decorators";
7-
import { hasValidAndroidSigning } from "../common/helpers";
8-
import { IProjectData, IProjectDataService } from "../definitions/project";
9-
import { IMigrateController } from "../definitions/migrate";
10-
import { IOptions, IPlatformValidationService } from "../declarations";
114
import { ICommand, ICommandParameter } from "../common/definitions/commands";
12-
import { IErrors, IHostInfo } from "../common/declarations";
5+
import {
6+
IKeyCommandHelper,
7+
IKeyCommandPlatform,
8+
} from "../common/definitions/key-commands";
139
import { IInjector } from "../common/definitions/yok";
10+
import { hasValidAndroidSigning } from "../common/helpers";
1411
import { injector } from "../common/yok";
12+
import {
13+
ANDROID_APP_BUNDLE_SIGNING_ERROR_MESSAGE,
14+
ANDROID_RELEASE_BUILD_ERROR_MESSAGE,
15+
} from "../constants";
16+
import { IOptions, IPlatformValidationService } from "../declarations";
17+
import { IMigrateController } from "../definitions/migrate";
18+
import { IProjectData, IProjectDataService } from "../definitions/project";
1519

1620
export class RunCommandBase implements ICommand {
17-
private liveSyncCommandHelperAdditionalOptions: ILiveSyncCommandHelperAdditionalOptions = <
18-
ILiveSyncCommandHelperAdditionalOptions
19-
>{};
21+
private liveSyncCommandHelperAdditionalOptions: ILiveSyncCommandHelperAdditionalOptions =
22+
<ILiveSyncCommandHelperAdditionalOptions>{};
2023

2124
public platform: string;
2225
constructor(
@@ -26,15 +29,23 @@ export class RunCommandBase implements ICommand {
2629
private $liveSyncCommandHelper: ILiveSyncCommandHelper,
2730
private $migrateController: IMigrateController,
2831
private $options: IOptions,
29-
private $projectData: IProjectData
32+
private $projectData: IProjectData,
33+
private $keyCommandHelper: IKeyCommandHelper
3034
) {}
3135

3236
public allowedParameters: ICommandParameter[] = [];
3337
public async execute(args: string[]): Promise<void> {
34-
return this.$liveSyncCommandHelper.executeCommandLiveSync(
38+
await this.$liveSyncCommandHelper.executeCommandLiveSync(
3539
this.platform,
3640
this.liveSyncCommandHelperAdditionalOptions
3741
);
42+
43+
if (process.env.NS_IS_INTERACTIVE) {
44+
this.$keyCommandHelper.attachKeyCommands(
45+
this.platform as IKeyCommandPlatform,
46+
"run"
47+
);
48+
}
3849
}
3950

4051
public async canExecute(args: string[]): Promise<boolean> {

lib/commands/start.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ICommand, ICommandParameter } from "../common/definitions/commands";
2+
import { printHeader } from "../common/header";
3+
import { injector } from "../common/yok";
4+
import { IStartService } from "../definitions/start-service";
5+
6+
export class StartCommand implements ICommand {
7+
constructor(private $startService: IStartService) {}
8+
async execute(args: string[]): Promise<void> {
9+
printHeader();
10+
this.$startService.start();
11+
return;
12+
}
13+
allowedParameters: ICommandParameter[];
14+
async canExecute?(args: string[]): Promise<boolean> {
15+
return true;
16+
}
17+
}
18+
19+
injector.registerCommand("start", StartCommand);

lib/commands/typings.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IOptions } from "../declarations";
1+
import { IOptions, IStaticConfig } from "../declarations";
22
import { IChildProcess, IFileSystem, IHostInfo } from "../common/declarations";
33
import { ICommand, ICommandParameter } from "../common/definitions/commands";
44
import { injector } from "../common/yok";
@@ -14,7 +14,8 @@ export class TypingsCommand implements ICommand {
1414
private $projectData: IProjectData,
1515
private $mobileHelper: Mobile.IMobileHelper,
1616
private $childProcess: IChildProcess,
17-
private $hostInfo: IHostInfo
17+
private $hostInfo: IHostInfo,
18+
private $staticConfig: IStaticConfig
1819
) {}
1920

2021
public async execute(args: string[]): Promise<void> {
@@ -122,11 +123,9 @@ export class TypingsCommand implements ICommand {
122123
path.resolve(this.$projectData.projectDir, "typings", "ios")
123124
);
124125

125-
const nsPath = path.resolve(__dirname, "../../bin/nativescript.js");
126-
127126
await this.$childProcess.spawnFromEvent(
128127
"node",
129-
[nsPath, "build", "ios"],
128+
[this.$staticConfig.cliBinPath, "build", "ios"],
130129
"exit",
131130
{
132131
env: {

lib/common/definitions/config.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare module Config {
1010
TRACK_FEATURE_USAGE_SETTING_NAME: string;
1111
ERROR_REPORT_SETTING_NAME: string;
1212
version: string;
13+
cliBinPath: string;
1314
getAdbFilePath(): Promise<string>;
1415
disableAnalytics?: boolean;
1516
disableCommandHooks?: boolean;
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
export type IKeyCommandPlatform = "Android" | "iOS" | "all";
2+
export type IKeysLowerCase =
3+
| "a"
4+
| "b"
5+
| "c"
6+
| "d"
7+
| "e"
8+
| "f"
9+
| "g"
10+
| "h"
11+
| "i"
12+
| "j"
13+
| "k"
14+
| "l"
15+
| "m"
16+
| "n"
17+
| "o"
18+
| "p"
19+
| "q"
20+
| "r"
21+
| "s"
22+
| "t"
23+
| "u"
24+
| "v"
25+
| "w"
26+
| "x"
27+
| "y"
28+
| "z";
29+
30+
export type IKeysUpperCase = Uppercase<IKeysLowerCase>;
31+
32+
export const enum SpecialKeys {
33+
CtrlC = "\u0003",
34+
QuestionMark = "?",
35+
}
36+
37+
export type IKeysSpecial = `${SpecialKeys}`;
38+
39+
export type IValidKeyName = IKeysLowerCase | IKeysUpperCase | IKeysSpecial;
40+
41+
export interface IKeyCommandHelper {
42+
attachKeyCommands: (
43+
platform: IKeyCommandPlatform,
44+
processType: SupportedProcessType
45+
) => void;
46+
47+
addOverride(key: IValidKeyName, execute: () => Promise<boolean>);
48+
removeOverride(key: IValidKeyName);
49+
printCommands(platform: IKeyCommandPlatform): void;
50+
}
51+
52+
export type SupportedProcessType = "start" | "run";
53+
54+
export interface IKeyCommand {
55+
key: IValidKeyName;
56+
platform: IKeyCommandPlatform;
57+
description: string;
58+
willBlockKeyCommandExecution?: boolean;
59+
execute(platform: string): Promise<void>;
60+
canExecute?: (processType: SupportedProcessType) => boolean;
61+
}

lib/common/definitions/yok.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IDisposable, IDictionary } from "../declarations";
22
import { ICommand } from "./commands";
3+
import { IKeyCommand, IValidKeyName } from "./key-commands";
34

45
interface IInjector extends IDisposable {
56
require(name: string, file: string): void;
@@ -8,6 +9,7 @@ interface IInjector extends IDisposable {
89
requirePublicClass(names: string | string[], file: string): void;
910
requireCommand(name: string, file: string): void;
1011
requireCommand(names: string[], file: string): void;
12+
requireKeyCommand(name: IValidKeyName, file: string): void;
1113
/**
1214
* Resolves an implementation by constructor function.
1315
* The injector will create new instances for every call.
@@ -22,10 +24,13 @@ interface IInjector extends IDisposable {
2224
resolve<T>(name: string, ctorArguments?: IDictionary<any>): T;
2325

2426
resolveCommand(name: string): ICommand;
27+
resolveKeyCommand(key: string): IKeyCommand;
2528
register(name: string, resolver: any, shared?: boolean): void;
2629
registerCommand(name: string, resolver: any): void;
2730
registerCommand(names: string[], resolver: any): void;
31+
registerKeyCommand(key: IValidKeyName, resolver: any): void;
2832
getRegisteredCommandsNames(includeDev: boolean): string[];
33+
getRegisteredKeyCommandsNames(): string[];
2934
dynamicCallRegex: RegExp;
3035
dynamicCall(call: string, args?: any[]): Promise<any>;
3136
isDefaultCommand(commandName: string): boolean;

lib/common/header.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { color, stripColors } from "../color";
2+
import { IStaticConfig } from "../declarations";
3+
import { injector } from "./yok";
4+
5+
export function printHeader() {
6+
if (process.env.HIDE_HEADER) return;
7+
8+
const $staticConfig: IStaticConfig = injector.resolve("$staticConfig");
9+
const version = $staticConfig.version;
10+
11+
const header = [
12+
color.dim("│ "),
13+
color.cyanBright.bold("{N} NativeScript "),
14+
color.whiteBright.bold("CLI"),
15+
color.dim(` [v${version}] `),
16+
// color.dim(" │"),
17+
].join("");
18+
const tagLine = [
19+
color.dim("│ "),
20+
color.dim(" → "),
21+
color.whiteBright.bold("Empower JavaScript with native APIs "),
22+
// color.dim(" │"),
23+
].join("");
24+
25+
const headerLength = stripColors(header).length;
26+
const tagLineLength = stripColors(tagLine).length;
27+
const width = Math.max(headerLength, tagLineLength);
28+
29+
console.info(" " + color.dim("┌" + "─".repeat(width - 1) + "┐"));
30+
console.info(
31+
" " + header + " ".repeat(width - headerLength) + color.dim("│")
32+
);
33+
console.info(
34+
" " + tagLine + " ".repeat(width - tagLineLength) + color.dim("│")
35+
);
36+
console.info(" " + color.dim("└" + "─".repeat(width - 1) + "┘"));
37+
}

lib/common/mobile/mobile-core/android-device-discovery.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ interface IAdbAndroidDeviceInfo {
1111

1212
export class AndroidDeviceDiscovery
1313
extends DeviceDiscovery
14-
implements Mobile.IAndroidDeviceDiscovery {
14+
implements Mobile.IAndroidDeviceDiscovery
15+
{
1516
private _devices: IAdbAndroidDeviceInfo[] = [];
1617
private isStarted: boolean;
1718

@@ -56,7 +57,6 @@ export class AndroidDeviceDiscovery
5657

5758
private async checkForDevices(): Promise<void> {
5859
const devices = await this.$adb.getDevices();
59-
6060
await this.checkCurrentData(devices);
6161
}
6262

0 commit comments

Comments
 (0)