8
头图

1 Introduction

Hello, my name is , WeChat search Vision" 160a0d7943d258 Follow me, focus on front-end technology sharing, a vision is to help the front-end to broaden its horizons to the forefront of the public account within 5 years. Welcome to add me on WeChat ruochuan12 for long-term exchange and learning.

This is the launch-editor source code ( 9) of the 160a0d7943d2ea learning source code overall architecture series. Learning Source overall architecture series ( What are the must-see JS library ): jQuery , Underscore , lodash , Sentry , vuex , Axios , KOA , Redux . The term “overall architecture” seems a bit big. Let’s say it is the overall structure of the source code. The main thing is to learn the overall structure of the code, and not to delve into the implementation of other specific functions that are not the main line. This article learns the code of the actual warehouse. The next article should be " learn the overall architecture of Vuex 4 source code, and understand its principles and provide/inject principles ".

warehouse address of this article is : git clone https://github.com/lxchuan12/open-in-editor.git , the best way to read this article, clone the warehouse and debug by yourself, easy to absorb and digest.

If someone talks about how to read the source code, if you are reading the article, you can recommend my source code series article, it is really not reporting 160a0d7943d338.

My article is written as far as possible so that readers who want to read the source code but don't know how to read it can understand. I always recommend using build the environment breakpoint debugging source code to learn , where does not point , watching while debugging, instead of just looking at . As the goes: 160a0d7943d364 It is better to teach people to fish than to teach people to fish .

After reading this article, you will learn:

  1. How to solve the error of this function
  2. How to debug learning source code
  3. launch-editor-middleware、launch-editor and other realization principles

1.1 Scenarios where the source file corresponding to the page cannot be found for a short time

I don’t know if you have encountered such a scene. When you open a page developed by yourself (or your colleague), it is difficult to find the corresponding source file in a short time.

At this time, you may want to have click the page button to automatically use the editor to open the corresponding file function, that would be great.

And vue-devtools provides such a function, maybe you don't know. I don’t think a lot of people know it, because many people don’t use vue-devtools .

open-in-editor

You may ask, I don’t use vue . I use react Is there a similar function? Yes, please see react-dev-inspector . You might also ask, what editor supports Yeah, mainstream vscode、webstorm、atom、sublime are all supported, you can see more this list Editors Supported .

This article is based on learning the launch-editor source code, which knowing it, knowing why , and exploring the vue-devtools "open component in editor" function.

1.2 Briefly describe its principle in one sentence

code path/to/file

Briefly describe the principle in one sentence: using nodejs in child_process , execute a code path/to/file , and then the corresponding editor opens the corresponding file, and the corresponding editor executes ps x in the process ( Window uses Get-Process command 060a0d7943d53d) Of course, you can also specify the editor yourself.

1.3 The error resolution of the component that cannot be opened when the editor is opened

When you actually use this function, you may encounter an error saying that the file cannot be opened.

Could not open App.vue in the editor.

To specify an editor, specify the EDITOR env variable or add "editor" field to your Vue project config.

控制台不能打开编辑器的错误提示

Here is a description of the Windows computer, VSCode editor, and terminal tools used Ubuntu At the same time, I recommend my article Use ohmyzsh to create efficient terminal command line tool windows, ubuntu, mac systems, used .

solution to 160a0d7943d5fe is also simple, which is the English meaning of .

1.3.1 Method 1: First make sure that the editor you use can be opened with a command in the terminal. The article takes VSCode as an example

If your command line itself cannot run code such as 060a0d7943d658 to open the editor, it must be an error. At this time, you need to VSCode into the command line terminal.
The injection method is also simple. A small partner in my communication group provided a screenshot of mac

mac computer is VSCode command + shift + p , Windows is ctrl + shift + p . Then enter shell and choose to install code . As shown below:

Install 'code' command in PATH

So you can open VSCode in the terminal.

If you can open it in the terminal and use the command editor to open it, but it still reports an error, then there is a high probability that your editor is not recognized.
Then you can set the designated editor through method two.

1.3.2 Method 2: Specify the editor, specify the editor in the environment variable

In vue project, the corresponding article is: vue3-project , add the .env.delelopment file, and its content is EDITOR=code . highlights here under my vue-cli version 4.5.12 , as if vue-cli 3.5 above and only supports custom EDITOR this environment variable .

# .env.development
# 当然,我的命令行终端已经有了code这个命令。
EDITOR=code
There is no need to specify the corresponding path of the editor ( c/Users/lxchu/AppData/Local/Programs/Microsoft VS Code/bin/code ), because an error will be reported. Why do I get an error, because I read the source code and tried it. Because it will be truncated according to the space and become c/Users/lxchu/AppData/Local/Programs/Microsoft , of course an error will be reported.

It is also possible that your editor path has a Chinese path and an error is reported. You can add your editor path to the environment variable.

If you pass the above methods, the error reporting problem has not been resolved. Welcome to leave a message, or add me to ruochuan12 communicate. After all, the computer environment is different, it is difficult to ensure that everyone can perform normally, but we know the principle, it is easy to solve the problem .

Next, we explore the implementation principle of the "open component in editor" function from the source code point of view.

2. vue-devtools Open component in editor document

Before exploring the principle, let's take a look at the official document of vue-devtools

vuejs/vue-devtools
Documentation

Open component in editor

To enable this feature, follow this guide.

This guide writes that Vue CLI 3 is out of the box in .

Vue CLI 3 supports this feature out-of-the-box when running vue-cli-service serve.

It also wrote in detail how to use Webpack

# 1. Import the package:
var openInEditor = require('launch-editor-middleware')
# 2. In the devServer option, register the /__open-in-editor HTTP route:
devServer: {
  before (app) {
    app.use('/__open-in-editor', openInEditor())
  }
}
# 3. The editor to launch is guessed. You can also specify the editor app with the editor option. See the supported editors list.
# 用哪个编辑器打开会自动猜测。你也可以具体指明编辑器。这里显示更多的支持编辑器列表
openInEditor('code')
# 4. You can now click on the name of the component in the Component inspector pane (if the devtools knows about its file source, a tooltip will appear).
# 如果`vue-devtools`开发者工具有提示点击的组件的显示具体路径,那么你可以在编辑器打开。

It also wrote how to use Node.js

Node.js

You can use the launch-editor package to setup an HTTP route with the /__open-in-editor path. It will receive file as an URL variable.

For more information, see this guide .

3. Environmental preparation

Readers who are familiar with me all know that I am recommended for debugging to look at the source code , as the goes: 160a0d7943d975 does not point where . And debugging is generally written in great detail, hoping to help some people know how to read the source code. So I specially built a new warehouse open-in-editor git clone https://github.com/lxchuan12/open-in-editor.git for everyone to clone and learn.

Install vue-cli

npm install -g @vue/cli
# OR
yarn global add @vue/cli
node -V
# v14.16.0
vue -V 
# @vue/cli 4.5.12
vue create vue3-project
# 这里选择的是vue3、vue2也是一样的。
# Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)
npm install
# OR
yarn install

Here also explain my version of vscode.

code -v
1.55.2

Vue CLI 3 mentioned above, 160a0d7943d9dc can be out of the box and Webpack .

vue3-project/package.json has a debug button.

debug示意图

Select the first item, serve vue-cli-service serve .

Let's search for 'launch-editor-middleware' . Generally speaking node_modules cannot be searched, and need to be set. Of course there is also a simple way. That is, there is a setting icon on the right side of "Excluded files" "Use "Troubleshooting settings" and "Ignore files"", click down.

I won’t repeat the rest. You can read this answer: 160a0d7943da40 How to set vscode to search for files contained in node_modules?

At this time, this middleware vue3-project/node_modules/@vue/cli-service/lib/commands/serve.js

As shown below:

搜索node_modules下的文件 - launch-editor-middleware 中间件

4. vue-devtools out of the box with specific source code implementation

Next, let's look at the specific source code implementation Vue CLI 3 out of the box.

// vue3-project/node_modules/@vue/cli-service/lib/commands/serve.js
// 46行
const launchEditorMiddleware = require('launch-editor-middleware')
// 192行
before (app, server) {
    // launch editor support.
    // this works with vue-devtools & @vue/cli-overlay
    app.use('/__open-in-editor', launchEditorMiddleware(() => console.log(
        `To specify an editor, specify the EDITOR env variable or ` +
        `add "editor" field to your Vue project config.\n`
    )))
    // 省略若干代码...
}

When you click on vue-devtools , there will be a request, http://localhost:8080/__open-in-editor?file=src/App.vue , and the component will be opened without accident.

open src/App.vue in editor

Then we implemented launchEditorMiddleware

5. launch-editor-middleware

When looking at the source code, first look at the debug screenshot.

debug-launch

The role in the launch-editor-middleware middleware is to finally call launch-editor open the file.

// vue3-project/node_modules/launch-editor-middleware/index.js
const url = require('url')
const path = require('path')
const launch = require('launch-editor')

module.exports = (specifiedEditor, srcRoot, onErrorCallback) => {
  // specifiedEditor => 这里传递过来的则是 () => console.log() 函数
  // 所以和 onErrorCallback 切换下,把它赋值给错误回调函数
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }

  // 如果第二个参数是函数,同样把它赋值给错误回调函数
  // 这里传递过来的是undefined
  if (typeof srcRoot === 'function') {
    onErrorCallback = srcRoot
    srcRoot = undefined
  }

  // srcRoot 是传递过来的参数,或者当前node进程的目录
  srcRoot = srcRoot || process.cwd()

  // 最后返回一个函数, express 中间件
  return function launchEditorMiddleware (req, res, next) {
    // 省略 ...
  }
}

previous paragraph of 160a0d7943db71, this way of switching parameters is very common in many source codes. In order to facilitate the user to pass parameters when calling. Although there are multiple parameters, one or two can be passed.

You can set a breakpoint according to the situation. For example, here I will break launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)

// vue3-project/node_modules/launch-editor-middleware/index.js
module.exports = (specifiedEditor, srcRoot, onErrorCallback) => {
  // 省略上半部分
  return function launchEditorMiddleware (req, res, next) {
    // 根据请求解析出file路径
    const { file } = url.parse(req.url, true).query || {}
    // 如果没有文件路径,则报错
    if (!file) {
      res.statusCode = 500
      res.end(`launch-editor-middleware: required query param "file" is missing.`)
    } else {
      // 否则拼接路径,用launch打开。
      launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
      res.end()
    }
  }
}

6. launch-editor

Following the breakpoints, I came to the launchEditor function.

// vue3-project/node_modules/launch-editor/index.js
function launchEditor (file, specifiedEditor, onErrorCallback) {
  // 解析出文件路径和行号列号等信息
  const parsed = parseFile(file)
  let { fileName } = parsed
  const { lineNumber, columnNumber } = parsed

  // 判断文件是否存在,不存在,直接返回。
  if (!fs.existsSync(fileName)) {
    return
  }
  // 所以和 onErrorCallback 切换下,把它赋值给错误回调函数
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }
  // 包裹一层函数
  onErrorCallback = wrapErrorCallback(onErrorCallback)

  // 猜测当前进程运行的是哪个编辑器
  const [editor, ...args] = guessEditor(specifiedEditor)
  if (!editor) {
    onErrorCallback(fileName, null)
    return
  }
  // 省略剩余部分,后文再讲述...
}

6.1 wrapErrorCallback wrap error function callback

onErrorCallback = wrapErrorCallback(onErrorCallback)

This code is to pass the error callback function, wrapErrorCallback returned to a new function, and wrapErrorCallback executed, onErrorCallback(cb) is executed.

I believe that readers can understand, I come alone to tell, mainly because this form wrapped in many functions in the source code are common .

This is the code position output by the Could not open App.vue in the editor. at the beginning of the article.

// vue3-project/node_modules/launch-editor/index.js
function wrapErrorCallback (cb) {
  return (fileName, errorMessage) => {
    console.log()
    console.log(
      chalk.red('Could not open ' + path.basename(fileName) + ' in the editor.')
    )
    if (errorMessage) {
      if (errorMessage[errorMessage.length - 1] !== '.') {
        errorMessage += '.'
      }
      console.log(
        chalk.red('The editor process exited with an error: ' + errorMessage)
      )
    }
    console.log()
    if (cb) cb(fileName, errorMessage)
  }
}

6.2 guessEditor guess the editor currently in use

This function mainly does the following four things:

  1. If the editor is specified, it will be parsed and returned.
  2. Find out which editor is running in the current process. macOS and Linux use ps x command
    windows use Get-Process command
  3. If none is found, use process.env.VISUAL or process.env.EDITOR . This is why the initial error prompts that you can use environment variables to specify the editor.
  4. In the end [null] it is not found, and an error will be reported.
const [editor, ...args] = guessEditor(specifiedEditor)
if (!editor) {
    onErrorCallback(fileName, null)
    return
}
// vue3-project/node_modules/launch-editor/guess.js
const shellQuote = require('shell-quote')
const childProcess = require('child_process')

module.exports = function guessEditor (specifiedEditor) {
  // 如果指定了编辑器,则解析一下,这里没有传入。如果自己指定了路径。
  // 比如 c/Users/lxchu/AppData/Local/Programs/Microsoft VS Code/bin/code 
  //   会根据空格切割成 c/Users/lxchu/AppData/Local/Programs/Microsoft
  if (specifiedEditor) {
    return shellQuote.parse(specifiedEditor)
  }
  // We can find out which editor is currently running by:
  // `ps x` on macOS and Linux
  // `Get-Process` on Windows
  try {
    //  代码有删减
    if (process.platform === 'darwin') {
      const output = childProcess.execSync('ps x').toString()
      // 省略
    } else if (process.platform === 'win32') {
      const output = childProcess
        .execSync('powershell -Command "Get-Process | Select-Object Path"', {
          stdio: ['pipe', 'pipe', 'ignore']
        })
        .toString()
        // 省略
    } else if (process.platform === 'linux') {
      const output = childProcess
        .execSync('ps x --no-heading -o comm --sort=comm')
        .toString()
    }
  } catch (error) {
    // Ignore...
  }

  // Last resort, use old skool env vars
  if (process.env.VISUAL) {
    return [process.env.VISUAL]
  } else if (process.env.EDITOR) {
    return [process.env.EDITOR]
  }

  return [null]
}

After reading the guessEditor function, let's look launch-editor rest of 060a0d7943dd60.

6.3 The rest of launch-editor

You don't need to look at the following code carefully, just look at it carefully when debugging.

// vue3-project/node_modules/launch-editor/index.js
function launchEditor(){
  //  省略上部分...
  if (
    process.platform === 'linux' &&
    fileName.startsWith('/mnt/') &&
    /Microsoft/i.test(os.release())
  ) {
    // Assume WSL / "Bash on Ubuntu on Windows" is being used, and
    // that the file exists on the Windows file system.
    // `os.release()` is "4.4.0-43-Microsoft" in the current release
    // build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364
    // When a Windows editor is specified, interop functionality can
    // handle the path translation, but only if a relative path is used.
    fileName = path.relative('', fileName)
  }

  if (lineNumber) {
    const extraArgs = getArgumentsForPosition(editor, fileName, lineNumber, columnNumber)
    args.push.apply(args, extraArgs)
  } else {
    args.push(fileName)
  }

  if (_childProcess && 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.
    _childProcess.kill('SIGKILL')
  }

  if (process.platform === 'win32') {
    // On Windows, launch the editor in a shell because spawn can only
    // launch .exe files.
    _childProcess = childProcess.spawn(
      'cmd.exe',
      ['/C', editor].concat(args),
      { stdio: 'inherit' }
    )
  } else {
    _childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
  }
  _childProcess.on('exit', function (errorCode) {
    _childProcess = null

    if (errorCode) {
      onErrorCallback(fileName, '(code ' + errorCode + ')')
    }
  })

  _childProcess.on('error', function (error) {
    onErrorCallback(fileName, error.message)
  })
}

In this large segment, the main thing is following code , using the sub-process module. Simply put, the subprocess module has the ability to execute commands.

const childProcess = require('child_process')

if (process.platform === 'win32') {
    // On Windows, launch the editor in a shell because spawn can only
    // launch .exe files.
    _childProcess = childProcess.spawn(
        'cmd.exe',
        ['/C', editor].concat(args),
        { stdio: 'inherit' }
    )
    } else {
    _childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
}

At this point in the writing, it is basically close to the end. The principle is actually to use nodejs in child_process to execute a command code path/to/file

7. Summary

Here is a summary: First of all, at the beginning of the article, it proposes "the scenario where the source file corresponding to the page cannot be found in a short time", and gives a solution to the error situation that is easy to encounter.
Secondly, configure the debug learning environment along with the vue-devtools especially the capital used in yyx990803 / Launch Editor- .

7.1 Briefly describe its principle in one sentence

Let's review the content of the principle at the beginning.

code path/to/file

Briefly explain the principle in one sentence: using nodejs in child_process , execute a code path/to/file , and then the corresponding editor opens the corresponding file, and the corresponding editor executes ps x in the process ( Window uses Get-Process ) command. Of course, you can also specify the editor yourself.

What can be done in the end.

You can look at umijs/launch-editor and react-dev-utils/launchEditor.js . Their codes are almost similar.

You can also use Node.js do some work such as improving development efficiency, and you can learn child_process and other modules at the same time.

also don't imprison your own thinking, imprison the front end in the page, you should broaden your field of vision .

Node.js for our front-end people to explore operating files, operating networks, etc.

If readers find something wrong or can be improved, or if there is no clear place, please comment and point out. In addition, I feel that it is well written, and it is a little helpful to you. You can like, comment, forward and share. It is also a kind of support for me. Thank you very much. If you can pay attention to my front-end public Vision" 160a0d7943df33, it would be better.

on

Hello, I’m , WeChat search Vision" 160a0d7943df8e Follow me, focus on front-end technology sharing, a vision is to help the front-end to broaden its horizons to the forefront of the public account within 5 years. Welcome to add me on WeChat ruochuan12 for long-term exchange and learning.
There are mainly the following series of articles: learning source code overall architecture series , annual summary , JS basic series

Reference link

yyx990803/launch-editor

umijs/launch-editor

vuejs/vue-devtools

vue-devtools open-in-editor.md

"Open in editor" button doesn't work in Win 10 with VSCode if installation path contains spaces

react-dev-utils/launchEditor.js


若川
7k 声望3.2k 粉丝

你好,我是若川。写有 《学习源码整体架构系列》 几十篇。