microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
test-microbuild1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commands/util/command.ts

153lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import assert = require("assert");
5import * as vscode from "vscode";
6import { EntryPointHandler } from "../../../common/entryPointHandler";
7import { ErrorHelper } from "../../../common/error/errorHelper";
8import { InternalError } from "../../../common/error/internalError";
9import { InternalErrorCode } from "../../../common/error/internalErrorCode";
10import { AppLauncher } from "../../appLauncher";
11import { PlatformType } from "../../launchArgs";
12import { OutputChannelLogger } from "../../log/OutputChannelLogger";
13import { TelemetryGenerator } from "../../../common/telemetryGenerators";
14import { selectProject } from ".";
15
16export abstract class Command<ArgT extends unknown[] = never[]> {
17 private static instances = new Map<Command, unknown>();
18
19 static formInstance<T extends { prototype: unknown }>(this: T): T["prototype"] {
20 // 'any' because TypeScript is wrong
21 // workaround from https://github.com/microsoft/TypeScript/issues/5863
22 const that = this as any;
23 const result = Command.instances.get(that) || new that();
24 Command.instances.set(that, result);
25 return result;
26 }
27
28 abstract readonly codeName: string;
29 abstract readonly label: string;
30 abstract readonly error: InternalError;
31
32 get platform(): string {
33 return (
34 [
35 PlatformType.Android,
36 PlatformType.iOS,
37 PlatformType.Exponent,
38 PlatformType.macOS,
39 PlatformType.Windows,
40 ].find(it => this.codeName.toLowerCase().includes(it.toLowerCase())) || ""
41 );
42 }
43
44 private entryPointHandler?: EntryPointHandler;
45
46 /** Initialize project property before executing command */
47 protected requiresProject = true;
48
49 /** Throw an Error if workspace is not trusted before executing command */
50 protected requiresTrust = true;
51
52 protected project?: AppLauncher;
53
54 protected constructor() {}
55
56 protected createHandler(fn = this.baseFn.bind(this)): (...args: ArgT) => Promise<void> {
57 return async (...args: ArgT): Promise<void> => {
58 assert(this.entryPointHandler, "this.entryPointHandler is not defined");
59
60 const resultFn = async (generator: TelemetryGenerator) => {
61 try {
62 await this.onBeforeExecute(...args);
63 await fn.bind(this)(...args);
64 } catch (error) {
65 switch (error.errorCode) {
66 case InternalErrorCode.CommandCanceled:
67 generator.addError(error);
68 return;
69 default:
70 throw error;
71 }
72 }
73 };
74
75 OutputChannelLogger.getMainChannel().debug(`Run command: ${this.codeName}`);
76
77 await this.entryPointHandler.runFunctionWExtProps(
78 `commandPalette.${this.codeName}`,
79 {
80 platform: {
81 value: this.platform,
82 isPii: false,
83 },
84 },
85 this.error,
86 resultFn.bind(this),
87 );
88 };
89 }
90
91 // eslint-disable-next-line @typescript-eslint/no-unused-vars
92 protected async onBeforeExecute(...args: ArgT): Promise<void> {
93 if (this.requiresProject) {
94 this.project = await this.selectProject();
95 }
96
97 if (this.requiresTrust && !isWorkspaceTrusted()) {
98 throw ErrorHelper.getInternalError(
99 InternalErrorCode.WorkspaceIsNotTrusted,
100 this.project?.getPackager().getProjectPath() || undefined,
101 this.label,
102 );
103 }
104
105 function isWorkspaceTrusted() {
106 // Remove after updating supported VS Code engine version to 1.57.0
107 if (typeof (vscode.workspace as any).isTrusted === "boolean") {
108 return (vscode.workspace as any).isTrusted;
109 }
110
111 return true;
112 }
113 }
114
115 protected async selectProject(): Promise<AppLauncher> {
116 try {
117 return await selectProject();
118 } catch (error) {
119 switch (error.errorCode) {
120 case InternalErrorCode.UserInputCanceled:
121 throw ErrorHelper.getNestedError(
122 error,
123 InternalErrorCode.CommandCanceled,
124 this.label,
125 );
126 default:
127 throw error;
128 }
129 }
130 }
131
132 abstract baseFn(...args: ArgT): Promise<void>;
133
134 /** Execute base command without telemetry */
135 async executeLocally(...args: ArgT): Promise<void> {
136 await this.onBeforeExecute(...args);
137 await this.baseFn(...args);
138 }
139
140 public register = (() => {
141 let isCalled = false;
142 return (entryPointHandler: EntryPointHandler) => {
143 this.entryPointHandler = entryPointHandler;
144
145 assert(!isCalled, "Command can only be registered once");
146 isCalled = true;
147 return vscode.commands.registerCommand(
148 `reactNative.${this.codeName}`,
149 this.createHandler(),
150 );
151 };
152 })();
153}
154