2
2
// Licensed under the MIT License. See License.txt in the project root for license information.
3
3
4
4
import * as asyncFs from "@ts-common/fs"
5
- import * as jsonParser from "@ts-common/json-parser"
6
5
import { getFilePosition } from "@ts-common/source-map"
7
6
import * as child_process from "child_process"
8
7
import * as fs from "fs"
9
8
import JSON_Pointer from "json-pointer"
10
9
import * as jsonRefs from "json-refs"
11
10
import * as os from "os"
12
11
import * as path from "path"
13
- import { quote } from "shell-quote"
14
12
import * as sourceMap from "source-map"
15
13
import * as util from "util"
16
14
import { log } from "../util/logging"
17
15
import { ResolveSwagger } from "../util/resolveSwagger"
18
16
import { pathToJsonPointer } from "../util/utils"
19
17
const _ = require ( "lodash" )
20
18
21
- const exec = util . promisify ( child_process . exec )
19
+ const execFile = util . promisify ( child_process . execFile )
22
20
23
21
export type Options = {
24
22
readonly consoleLogLevel ?: unknown
@@ -83,28 +81,6 @@ const updateChangeProperties = (change: ChangeProperties, pf: ProcessedFile): Ch
83
81
}
84
82
}
85
83
86
- /**
87
- * Safely escapes shell arguments for cross-platform compatibility
88
- * @param arg The argument to escape
89
- * @returns The safely escaped argument
90
- */
91
- function escapeShellArg ( arg : string ) : string {
92
- if ( typeof arg !== "string" ) {
93
- throw new Error ( "Argument must be a string" )
94
- }
95
-
96
- if ( process . platform === "win32" ) {
97
- // For Windows cmd.exe, wrap in double quotes and escape internal quotes
98
- // This handles paths with spaces and special characters safely
99
- // Double quotes are escaped by doubling them in Windows
100
- return `"${ arg . replace ( / " / g, '""' ) } "`
101
- } else {
102
- // On Unix-like systems, use shell-quote for proper escaping
103
- // shell-quote handles all edge cases including spaces, special chars, etc.
104
- return quote ( [ arg ] )
105
- }
106
- }
107
-
108
84
/**
109
85
* @class
110
86
* Open API Diff class.
@@ -165,19 +141,19 @@ export class OpenApiDiff {
165
141
}
166
142
167
143
/**
168
- * Gets path to the autorest application.
144
+ * Gets file and args to the autorest application.
169
145
*
170
- * @returns {string } Path to the autorest app.js file.
146
+ * @returns {{ file: string; args: string[] } } File and args to the autorest app.js file.
171
147
*/
172
- public autoRestPath ( ) : string {
173
- log . silly ( `autoRestPath is being called` )
148
+ public autoRestFileArgs ( ) : { file : string ; args : string [ ] } {
149
+ log . silly ( `autoRestFileArgs is being called` )
174
150
175
151
// When oad is installed globally
176
152
{
177
153
const result = path . join ( __dirname , ".." , ".." , ".." , "node_modules" , "autorest" , "dist" , "app.js" )
178
154
if ( fs . existsSync ( result ) ) {
179
155
log . silly ( `Found autoRest:${ result } ` )
180
- return ` node ${ escapeShellArg ( result ) } `
156
+ return { file : " node" , args : [ result ] }
181
157
}
182
158
}
183
159
@@ -186,7 +162,7 @@ export class OpenApiDiff {
186
162
const result = path . join ( __dirname , ".." , ".." , ".." , ".." , ".." , "autorest" , "dist" , "app.js" )
187
163
if ( fs . existsSync ( result ) ) {
188
164
log . silly ( `Found autoRest:${ result } ` )
189
- return ` node ${ escapeShellArg ( result ) } `
165
+ return { file : " node" , args : [ result ] }
190
166
}
191
167
}
192
168
@@ -195,12 +171,12 @@ export class OpenApiDiff {
195
171
const result = path . resolve ( "node_modules/.bin/autorest" )
196
172
if ( fs . existsSync ( result ) ) {
197
173
log . silly ( `Found autoRest:${ result } ` )
198
- return escapeShellArg ( result )
174
+ return { file : result , args : [ ] }
199
175
}
200
176
}
201
177
202
178
// Assume that autorest is in the path
203
- return "autorest"
179
+ return { file : "autorest" , args : [ ] }
204
180
}
205
181
206
182
/**
@@ -211,7 +187,7 @@ export class OpenApiDiff {
211
187
public openApiDiffDllPath ( ) : string {
212
188
log . silly ( `openApiDiffDllPath is being called` )
213
189
214
- return escapeShellArg ( path . join ( __dirname , ".." , ".." , ".." , "dlls" , "OpenApiDiff.dll" ) )
190
+ return path . join ( __dirname , ".." , ".." , ".." , "dlls" , "OpenApiDiff.dll" )
215
191
}
216
192
217
193
/**
@@ -250,16 +226,24 @@ export class OpenApiDiff {
250
226
const outputFolder = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , "oad-" ) )
251
227
const outputFilePath = path . join ( outputFolder , `${ outputFileName } .json` )
252
228
const outputMapFilePath = path . join ( outputFolder , `${ outputFileName } .map` )
253
- // Cross-platform shell argument escaping - behavior is validated in shellEscapingTest.ts
254
- const autoRestCmd = tagName
255
- ? `${ this . autoRestPath ( ) } ${ escapeShellArg ( swaggerPath ) } --v2 --tag=${ escapeShellArg ( tagName ) } --output-artifact=swagger-document.json` +
256
- ` --output-artifact=swagger-document.map --output-file=${ escapeShellArg ( outputFileName ) } --output-folder=${ escapeShellArg ( outputFolder ) } `
257
- : `${ this . autoRestPath ( ) } --v2 --input-file=${ escapeShellArg ( swaggerPath ) } --output-artifact=swagger-document.json` +
258
- ` --output-artifact=swagger-document.map --output-file=${ escapeShellArg ( outputFileName ) } --output-folder=${ escapeShellArg ( outputFolder ) } `
259
229
260
- log . debug ( `Executing: "${ autoRestCmd } "` )
230
+ const { file : autoRestFile , args : autoRestArgs } = this . autoRestFileArgs ( )
231
+
232
+ const swaggerArgs = tagName ? [ swaggerPath , `--tag=${ tagName } ` ] : [ `--input-file=${ swaggerPath } ` ]
233
+
234
+ const commonArgs = [
235
+ "--v2" ,
236
+ "--output-artifact=swagger-document.json" ,
237
+ "--output-artifact=swagger-document.map" ,
238
+ `--output-file=${ outputFileName } ` ,
239
+ `--output-folder=${ outputFolder } `
240
+ ]
241
+
242
+ const args = [ ...autoRestArgs , ...swaggerArgs , ...commonArgs ]
243
+
244
+ log . debug ( `Executing: "${ autoRestFile } ${ args . join ( " " ) } "` )
261
245
262
- const { stderr } = await exec ( autoRestCmd , {
246
+ const { stderr } = await execFile ( autoRestFile , args , {
263
247
encoding : "utf8" ,
264
248
maxBuffer : 1024 * 1024 * 64 ,
265
249
env : { ...process . env , NODE_OPTIONS : "--max-old-space-size=8192" }
@@ -319,10 +303,11 @@ export class OpenApiDiff {
319
303
throw new Error ( `File "${ newSwagger } " not found.` )
320
304
}
321
305
322
- const cmd = `${ this . dotNetPath ( ) } ${ this . openApiDiffDllPath ( ) } -o ${ oldSwagger } -n ${ newSwagger } `
306
+ const file = this . dotNetPath ( )
307
+ const args = [ this . openApiDiffDllPath ( ) , "-o" , oldSwagger , "-n" , newSwagger ]
323
308
324
- log . debug ( `Executing: "${ cmd } "` )
325
- const { stdout } = await exec ( cmd , { encoding : "utf8" , maxBuffer : 1024 * 1024 * 64 } )
309
+ log . debug ( `Executing: "${ file } ${ args . join ( " " ) } "` )
310
+ const { stdout } = await execFile ( file , args , { encoding : "utf8" , maxBuffer : 1024 * 1024 * 64 } )
326
311
const resultJson = JSON . parse ( stdout ) as Messages
327
312
328
313
const updatedJson = resultJson . map ( message => ( {
0 commit comments