@@ -11,9 +11,9 @@ import * as https from 'https';
11
11
import * as createHttpsProxyAgent from 'https-proxy-agent' ;
12
12
import * as path from 'path' ;
13
13
import { URL } from 'url' ;
14
- import { DownloadOptions , DownloadPlatform , downloadAndUnzipVSCode } from './download' ;
14
+ import { DownloadOptions , DownloadPlatform , defaultCachePath , downloadAndUnzipVSCode } from './download' ;
15
15
import * as request from './request' ;
16
- import { TestOptions , getProfileArguments } from './runTest' ;
16
+ import { TestOptions } from './runTest' ;
17
17
18
18
export let systemDefaultPlatform : DownloadPlatform ;
19
19
@@ -219,7 +219,39 @@ export function resolveCliArgsFromVSCodeExecutablePath(
219
219
return args ;
220
220
}
221
221
222
- export type RunVSCodeCommandOptions = Partial < DownloadOptions > & { spawn ?: SpawnOptions } ;
222
+ export interface RunVSCodeCommandOptions extends Partial < DownloadOptions > {
223
+ /**
224
+ * Additional options to pass to `child_process.spawn`
225
+ */
226
+ spawn ?: SpawnOptions ;
227
+
228
+ /**
229
+ * Whether VS Code should be launched using default settings and extensions
230
+ * installed on this machine. If `false`, then separate directories will be
231
+ * used inside the `.vscode-test` folder within the project.
232
+ *
233
+ * Defaults to `false`.
234
+ */
235
+ reuseMachineInstall ?: boolean ;
236
+ }
237
+
238
+ /** Adds the extensions and user data dir to the arguments for the VS Code CLI */
239
+ export function getProfileArguments ( args : readonly string [ ] ) {
240
+ const out : string [ ] = [ ] ;
241
+ if ( ! hasArg ( 'extensions-dir' , args ) ) {
242
+ out . push ( `--extensions-dir=${ path . join ( defaultCachePath , 'extensions' ) } ` ) ;
243
+ }
244
+
245
+ if ( ! hasArg ( 'user-data-dir' , args ) ) {
246
+ out . push ( `--user-data-dir=${ path . join ( defaultCachePath , 'user-data' ) } ` ) ;
247
+ }
248
+
249
+ return out ;
250
+ }
251
+
252
+ export function hasArg ( argName : string , argList : readonly string [ ] ) {
253
+ return argList . some ( ( a ) => a === `--${ argName } ` || a . startsWith ( `--${ argName } =` ) ) ;
254
+ }
223
255
224
256
export class VSCodeCommandError extends Error {
225
257
constructor (
@@ -233,20 +265,30 @@ export class VSCodeCommandError extends Error {
233
265
}
234
266
235
267
/**
236
- * Runs a VS Code command, and returns its output
268
+ * Runs a VS Code command, and returns its output.
269
+ *
237
270
* @throws a {@link VSCodeCommandError} if the command fails
238
271
*/
239
- export async function runVSCodeCommand ( args : string [ ] , options : RunVSCodeCommandOptions = { } ) {
240
- const vscodeExecutablePath = await downloadAndUnzipVSCode ( options ) ;
241
- const [ cli , ...baseArgs ] = resolveCliArgsFromVSCodeExecutablePath ( vscodeExecutablePath ) ;
272
+ export async function runVSCodeCommand ( _args : readonly string [ ] , options : RunVSCodeCommandOptions = { } ) {
273
+ const args = _args . slice ( ) ;
242
274
243
- const shell = process . platform === 'win32' ;
275
+ let executable = await downloadAndUnzipVSCode ( options ) ;
276
+ let shell = false ;
277
+ if ( ! options . reuseMachineInstall ) {
278
+ args . push ( ...getProfileArguments ( args ) ) ;
279
+ }
280
+
281
+ // Unless the user is manually running tests or extension development, then resolve to the CLI script
282
+ if ( ! hasArg ( 'extensionTestsPath' , args ) && ! hasArg ( 'extensionDevelopmentPath' , args ) ) {
283
+ executable = resolveCliPathFromVSCodeExecutablePath ( executable , options ?. platform ?? systemDefaultPlatform ) ;
284
+ shell = process . platform === 'win32' ; // CVE-2024-27980
285
+ }
244
286
245
287
return new Promise < { stdout : string ; stderr : string } > ( ( resolve , reject ) => {
246
288
let stdout = '' ;
247
289
let stderr = '' ;
248
290
249
- const child = spawn ( shell ? `"${ cli } "` : cli , [ ... baseArgs , ... args ] , {
291
+ const child = spawn ( shell ? `"${ executable } "` : executable , args , {
250
292
stdio : 'pipe' ,
251
293
shell,
252
294
windowsHide : true ,
0 commit comments