site-panzai/node_modules/launch-editor/index.js

122 lines
3.3 KiB
JavaScript
Raw Normal View History

2026-11-13 13:57:04 +01:00
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
*
* Modified by Yuxi Evan You
*/
const fs = require('fs')
const path = require('path')
const colors = require('picocolors')
const childProcess = require('child_process')
const guessEditor = require('./guess')
const getArgumentsForFile = require('./get-args')
const constructWindowsLaunchCommand = require('./construct-windows-launch-command')
const parseFile = require('./parse-file')
function wrapErrorCallback(cb) {
return (fileName, errorMessage) => {
console.log()
console.log(colors.red('Could not open ' + path.basename(fileName) + ' in the editor.'))
if (errorMessage) {
if (errorMessage[errorMessage.length - 1] !== '.') {
errorMessage += '.'
}
console.log(colors.red('The editor process exited with an error: ' + errorMessage))
}
console.log()
if (cb) cb(fileName, errorMessage)
}
}
function isTerminalEditor(editor) {
switch (editor) {
case 'vim':
case 'emacs':
case 'nano':
return true
}
return false
}
let currentChildProcess = null
function launchEditor(file, specifiedEditor, onErrorCallback) {
const { fileName, lineNumber, columnNumber } = parseFile(file)
if (typeof specifiedEditor === 'function') {
onErrorCallback = specifiedEditor
specifiedEditor = undefined
}
onErrorCallback = wrapErrorCallback(onErrorCallback)
if (process.platform === 'win32' && fileName.startsWith('\\\\')) {
return onErrorCallback(
fileName,
'UNC paths are not supported on Windows to avoid security issues. ' +
'See https://github.com/vitejs/launch-editor/tree/main/packages/launch-editor#unc-paths-on-windows for details.',
)
}
if (!fs.existsSync(fileName)) {
return
}
const [editor, ...args] = guessEditor(specifiedEditor)
if (!editor) {
onErrorCallback(fileName, null)
return
}
const extraArgs = getArgumentsForFile(editor, fileName, lineNumber, columnNumber)
args.push.apply(args, extraArgs)
let launchCommand
if (process.platform === 'win32') {
try {
launchCommand = constructWindowsLaunchCommand(editor, args)
} catch (error) {
onErrorCallback(fileName, error.message)
return
}
}
if (currentChildProcess && isTerminalEditor(editor)) {
// There's an existing editor process already and it's attached
// to the terminal, so go kill it. Otherwise two separate editor
// instances attach to the stdin/stdout which gets confusing.
currentChildProcess.kill('SIGKILL')
}
if (process.platform === 'win32') {
currentChildProcess = childProcess.exec(launchCommand, {
stdio: 'inherit',
shell: true,
})
} else {
currentChildProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
}
currentChildProcess.on('exit', function (errorCode) {
currentChildProcess = null
if (errorCode) {
onErrorCallback(fileName, '(code ' + errorCode + ')')
}
})
currentChildProcess.on('error', function (error) {
let { code, message } = error
if ('ENOENT' === code) {
message = `${message} ('${editor}' command does not exist in 'PATH')`
}
onErrorCallback(fileName, message)
})
}
module.exports = launchEditor