@theia/process 1.53.0-next.55 → 1.53.0-next.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,187 +1,187 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2020 Ericsson and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
-
17
- /*---------------------------------------------------------------------------------------------
18
- * Copyright (c) Microsoft Corporation. All rights reserved.
19
- * Licensed under the MIT License. See License.txt in the project root for license information.
20
- *--------------------------------------------------------------------------------------------*/
21
-
22
- /* eslint-disable no-null/no-null */
23
-
24
- import { injectable } from '@theia/core/shared/inversify';
25
- import {
26
- createShellCommandLine, BashQuotingFunctions, PowershellQuotingFunctions, CmdQuotingFunctions, ShellQuoting, ShellQuotedString, escapeForShell, ShellQuotingFunctions
27
- } from '../common/shell-quoting';
28
-
29
- export interface ProcessInfo {
30
- executable: string
31
- arguments: string[]
32
- }
33
-
34
- export interface CommandLineOptions {
35
- cwd: string
36
- args: string[]
37
- env?: {
38
- [key: string]: string | null
39
- }
40
- }
41
-
42
- /**
43
- * Create command lines ready to be sent to a shell's stdin for evaluation.
44
- */
45
- @injectable()
46
- export class ShellCommandBuilder {
47
-
48
- /**
49
- * Constructs a command line to run in a shell. The shell could be
50
- * re-used/long-lived, this means we cannot spawn a new process with a nice
51
- * and fresh environment, we need to encode environment modifications into
52
- * the returned command.
53
- *
54
- * Inspired by VS Code implementation, see:
55
- * https://github.com/microsoft/vscode/blob/f395cac4fff0721a8099126172c01411812bcb4a/src/vs/workbench/contrib/debug/node/terminals.ts#L79
56
- *
57
- * @param hostProcessInfo the host terminal process infos
58
- * @param commandOptions program to execute in the host terminal
59
- */
60
- buildCommand(hostProcessInfo: ProcessInfo | undefined, commandOptions: CommandLineOptions): string {
61
-
62
- const host = hostProcessInfo && hostProcessInfo.executable;
63
- const cwd = commandOptions.cwd;
64
-
65
- const args = commandOptions.args.map(value => ({
66
- value, quoting: ShellQuoting.Strong,
67
- } as ShellQuotedString));
68
-
69
- const env: Array<[string, string | null]> = [];
70
- if (commandOptions.env) {
71
- for (const key of Object.keys(commandOptions.env)) {
72
- env.push([key, commandOptions.env[key]]);
73
- }
74
- }
75
- if (host) {
76
- if (/(bash|wsl)(.exe)?$/.test(host)) {
77
- return this.buildForBash(args, cwd, env);
78
- } else if (/(ps|pwsh|powershell)(.exe)?$/i.test(host)) {
79
- return this.buildForPowershell(args, cwd, env);
80
- } else if (/cmd(.exe)?$/i.test(host)) {
81
- return this.buildForCmd(args, cwd, env);
82
- }
83
- }
84
- return this.buildForDefault(args, cwd, env);
85
- }
86
-
87
- protected buildForBash(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
88
- let command = '';
89
- if (cwd) {
90
- command += `cd ${BashQuotingFunctions.strong(cwd)} && `;
91
- }
92
- if (env?.length) {
93
- command += 'env';
94
- for (const [key, value] of env) {
95
- if (value === null) {
96
- command += ` -u ${BashQuotingFunctions.strong(key)}`;
97
- } else {
98
- command += ` ${BashQuotingFunctions.strong(`${key}=${value}`)}`;
99
- }
100
- }
101
- command += ' ';
102
- }
103
- command += this.createShellCommandLine(args, BashQuotingFunctions);
104
- return command;
105
- }
106
-
107
- protected buildForPowershell(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
108
- let command = '';
109
- if (cwd) {
110
- command += `cd ${PowershellQuotingFunctions.strong(cwd)}; `;
111
- }
112
- if (env?.length) {
113
- for (const [key, value] of env) {
114
- // Powershell requires special quoting when dealing with
115
- // environment variable names.
116
- const quotedKey = key
117
- .replace(/`/g, '````')
118
- .replace(/\?/g, '``?');
119
- if (value === null) {
120
- command += `Remove-Item \${env:${quotedKey}}; `;
121
- } else {
122
- command += `\${env:${quotedKey}}=${PowershellQuotingFunctions.strong(value)}; `;
123
- }
124
- }
125
- }
126
- command += '& ' + this.createShellCommandLine(args, PowershellQuotingFunctions);
127
- return command;
128
- }
129
-
130
- protected buildForCmd(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
131
- let command = '';
132
- if (cwd) {
133
- command += `cd ${CmdQuotingFunctions.strong(cwd)} && `;
134
- }
135
- // Current quoting mechanism only works within a nested `cmd` call:
136
- command += 'cmd /C "';
137
- if (env?.length) {
138
- for (const [key, value] of env) {
139
- if (value === null) {
140
- command += `set ${CmdQuotingFunctions.strong(key)}="" && `;
141
- } else {
142
- command += `set ${CmdQuotingFunctions.strong(`${key}=${value}`)} && `;
143
- }
144
- }
145
- }
146
- command += this.createShellCommandLine(args, CmdQuotingFunctions);
147
- command += '"';
148
- return command;
149
- }
150
-
151
- protected buildForDefault(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
152
- return this.buildForBash(args, cwd, env);
153
- }
154
-
155
- /**
156
- * This method will try to leave `arg[0]` unescaped if possible. The reason
157
- * is that shells like `cmd` expect their own commands like `dir` to be
158
- * unescaped.
159
- *
160
- * @returns empty string if `args` is empty, otherwise an escaped command.
161
- */
162
- protected createShellCommandLine(args: (string | ShellQuotedString)[], quotingFunctions: ShellQuotingFunctions): string {
163
- let command = '';
164
- if (args.length > 0) {
165
- const [exec, ...execArgs] = args;
166
- // Some commands like `dir` should not be quoted for `cmd` to understand:
167
- command += this.quoteExecutableIfNecessary(exec, quotingFunctions);
168
- if (execArgs.length > 0) {
169
- command += ' ' + createShellCommandLine(execArgs, quotingFunctions);
170
- }
171
- }
172
- return command;
173
- }
174
-
175
- protected quoteExecutableIfNecessary(exec: string | ShellQuotedString, quotingFunctions: ShellQuotingFunctions): string {
176
- return typeof exec === 'string' && !this.needsQuoting(exec) ? exec : escapeForShell(exec, quotingFunctions);
177
- }
178
-
179
- /**
180
- * If this method returns `false` then we definitely need quoting.
181
- *
182
- * May return false positives.
183
- */
184
- protected needsQuoting(arg: string): boolean {
185
- return /\W/.test(arg);
186
- }
187
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2020 Ericsson and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ /*---------------------------------------------------------------------------------------------
18
+ * Copyright (c) Microsoft Corporation. All rights reserved.
19
+ * Licensed under the MIT License. See License.txt in the project root for license information.
20
+ *--------------------------------------------------------------------------------------------*/
21
+
22
+ /* eslint-disable no-null/no-null */
23
+
24
+ import { injectable } from '@theia/core/shared/inversify';
25
+ import {
26
+ createShellCommandLine, BashQuotingFunctions, PowershellQuotingFunctions, CmdQuotingFunctions, ShellQuoting, ShellQuotedString, escapeForShell, ShellQuotingFunctions
27
+ } from '../common/shell-quoting';
28
+
29
+ export interface ProcessInfo {
30
+ executable: string
31
+ arguments: string[]
32
+ }
33
+
34
+ export interface CommandLineOptions {
35
+ cwd: string
36
+ args: string[]
37
+ env?: {
38
+ [key: string]: string | null
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Create command lines ready to be sent to a shell's stdin for evaluation.
44
+ */
45
+ @injectable()
46
+ export class ShellCommandBuilder {
47
+
48
+ /**
49
+ * Constructs a command line to run in a shell. The shell could be
50
+ * re-used/long-lived, this means we cannot spawn a new process with a nice
51
+ * and fresh environment, we need to encode environment modifications into
52
+ * the returned command.
53
+ *
54
+ * Inspired by VS Code implementation, see:
55
+ * https://github.com/microsoft/vscode/blob/f395cac4fff0721a8099126172c01411812bcb4a/src/vs/workbench/contrib/debug/node/terminals.ts#L79
56
+ *
57
+ * @param hostProcessInfo the host terminal process infos
58
+ * @param commandOptions program to execute in the host terminal
59
+ */
60
+ buildCommand(hostProcessInfo: ProcessInfo | undefined, commandOptions: CommandLineOptions): string {
61
+
62
+ const host = hostProcessInfo && hostProcessInfo.executable;
63
+ const cwd = commandOptions.cwd;
64
+
65
+ const args = commandOptions.args.map(value => ({
66
+ value, quoting: ShellQuoting.Strong,
67
+ } as ShellQuotedString));
68
+
69
+ const env: Array<[string, string | null]> = [];
70
+ if (commandOptions.env) {
71
+ for (const key of Object.keys(commandOptions.env)) {
72
+ env.push([key, commandOptions.env[key]]);
73
+ }
74
+ }
75
+ if (host) {
76
+ if (/(bash|wsl)(.exe)?$/.test(host)) {
77
+ return this.buildForBash(args, cwd, env);
78
+ } else if (/(ps|pwsh|powershell)(.exe)?$/i.test(host)) {
79
+ return this.buildForPowershell(args, cwd, env);
80
+ } else if (/cmd(.exe)?$/i.test(host)) {
81
+ return this.buildForCmd(args, cwd, env);
82
+ }
83
+ }
84
+ return this.buildForDefault(args, cwd, env);
85
+ }
86
+
87
+ protected buildForBash(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
88
+ let command = '';
89
+ if (cwd) {
90
+ command += `cd ${BashQuotingFunctions.strong(cwd)} && `;
91
+ }
92
+ if (env?.length) {
93
+ command += 'env';
94
+ for (const [key, value] of env) {
95
+ if (value === null) {
96
+ command += ` -u ${BashQuotingFunctions.strong(key)}`;
97
+ } else {
98
+ command += ` ${BashQuotingFunctions.strong(`${key}=${value}`)}`;
99
+ }
100
+ }
101
+ command += ' ';
102
+ }
103
+ command += this.createShellCommandLine(args, BashQuotingFunctions);
104
+ return command;
105
+ }
106
+
107
+ protected buildForPowershell(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
108
+ let command = '';
109
+ if (cwd) {
110
+ command += `cd ${PowershellQuotingFunctions.strong(cwd)}; `;
111
+ }
112
+ if (env?.length) {
113
+ for (const [key, value] of env) {
114
+ // Powershell requires special quoting when dealing with
115
+ // environment variable names.
116
+ const quotedKey = key
117
+ .replace(/`/g, '````')
118
+ .replace(/\?/g, '``?');
119
+ if (value === null) {
120
+ command += `Remove-Item \${env:${quotedKey}}; `;
121
+ } else {
122
+ command += `\${env:${quotedKey}}=${PowershellQuotingFunctions.strong(value)}; `;
123
+ }
124
+ }
125
+ }
126
+ command += '& ' + this.createShellCommandLine(args, PowershellQuotingFunctions);
127
+ return command;
128
+ }
129
+
130
+ protected buildForCmd(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
131
+ let command = '';
132
+ if (cwd) {
133
+ command += `cd ${CmdQuotingFunctions.strong(cwd)} && `;
134
+ }
135
+ // Current quoting mechanism only works within a nested `cmd` call:
136
+ command += 'cmd /C "';
137
+ if (env?.length) {
138
+ for (const [key, value] of env) {
139
+ if (value === null) {
140
+ command += `set ${CmdQuotingFunctions.strong(key)}="" && `;
141
+ } else {
142
+ command += `set ${CmdQuotingFunctions.strong(`${key}=${value}`)} && `;
143
+ }
144
+ }
145
+ }
146
+ command += this.createShellCommandLine(args, CmdQuotingFunctions);
147
+ command += '"';
148
+ return command;
149
+ }
150
+
151
+ protected buildForDefault(args: Array<string | ShellQuotedString>, cwd?: string, env?: Array<[string, string | null]>): string {
152
+ return this.buildForBash(args, cwd, env);
153
+ }
154
+
155
+ /**
156
+ * This method will try to leave `arg[0]` unescaped if possible. The reason
157
+ * is that shells like `cmd` expect their own commands like `dir` to be
158
+ * unescaped.
159
+ *
160
+ * @returns empty string if `args` is empty, otherwise an escaped command.
161
+ */
162
+ protected createShellCommandLine(args: (string | ShellQuotedString)[], quotingFunctions: ShellQuotingFunctions): string {
163
+ let command = '';
164
+ if (args.length > 0) {
165
+ const [exec, ...execArgs] = args;
166
+ // Some commands like `dir` should not be quoted for `cmd` to understand:
167
+ command += this.quoteExecutableIfNecessary(exec, quotingFunctions);
168
+ if (execArgs.length > 0) {
169
+ command += ' ' + createShellCommandLine(execArgs, quotingFunctions);
170
+ }
171
+ }
172
+ return command;
173
+ }
174
+
175
+ protected quoteExecutableIfNecessary(exec: string | ShellQuotedString, quotingFunctions: ShellQuotingFunctions): string {
176
+ return typeof exec === 'string' && !this.needsQuoting(exec) ? exec : escapeForShell(exec, quotingFunctions);
177
+ }
178
+
179
+ /**
180
+ * If this method returns `false` then we definitely need quoting.
181
+ *
182
+ * May return false positives.
183
+ */
184
+ protected needsQuoting(arg: string): boolean {
185
+ return /\W/.test(arg);
186
+ }
187
+ }