Skip to content

Commit 06097fd

Browse files
committed
fix(dev-overlay): Better handle edge-case file paths in launchEditor (#79526)
Without this, some files may fail to open correctly. Internal discussion: https://vercel.slack.com/archives/C07UWUAF95Z/p1747261772629689
1 parent bda731f commit 06097fd

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { escapeApplescriptStringFragment } from './launch-editor'
2+
3+
describe('applescript string escaping', () => {
4+
it('should escape strings correctly', () => {
5+
const result = escapeApplescriptStringFragment(`abc\\def"ghi\\\\`)
6+
expect(result).toBe(`abc\\\\def\\"ghi\\\\\\\\`)
7+
})
8+
})

packages/next/src/client/components/react-dev-overlay/utils/launch-editor.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,22 @@ function printInstructions(fileName: string, errorMessage: string | null) {
303303
console.log()
304304
}
305305

306-
function launchEditor(fileName: string, lineNumber: number, colNumber: number) {
306+
export function escapeApplescriptStringFragment(input: string): string {
307+
// The only two special characters in a quoted applescript string are
308+
// backslash and double quote. Both are escaped with a preceeding backslash.
309+
//
310+
// Some whitespace characters (like newlines) can be escaped (as `\n`), but
311+
// aren't required to be escaped (so we're not bothering to do that).
312+
//
313+
// https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_classes.html#//apple_ref/doc/uid/TP40000983-CH1g-BBCIAHJF:~:text=Special%20String%20Characters
314+
return input.replaceAll(/[\\"]/g, (original) => `\\${original}`)
315+
}
316+
317+
export function launchEditor(
318+
fileName: string,
319+
lineNumber: number,
320+
colNumber: number
321+
) {
307322
if (!fs.existsSync(fileName)) {
308323
return
309324
}
@@ -387,15 +402,12 @@ function launchEditor(fileName: string, lineNumber: number, colNumber: number) {
387402
})
388403
} else if (isTerminalEditor(editor)) {
389404
if (process.platform === 'darwin') {
405+
const escapedScript = escapeApplescriptStringFragment(
406+
shellQuote.quote([editor, ...args])
407+
)
390408
p = child_process.spawn(
391409
'osascript',
392-
[
393-
'-e',
394-
`tell application "Terminal" to do script "${shellQuote.quote([
395-
editor,
396-
...args,
397-
])}"`,
398-
],
410+
['-e', `tell application "Terminal" to do script "${escapedScript}"`],
399411
{ stdio: 'ignore' }
400412
)
401413
} else {
@@ -416,5 +428,3 @@ function launchEditor(fileName: string, lineNumber: number, colNumber: number) {
416428
})
417429
}
418430
}
419-
420-
export { launchEditor }

0 commit comments

Comments
 (0)