1. Introduction

Hello, I am Ruochuan, wechat search “Ruochuan vision” to follow me, focus on front-end technology sharing, a vision to help front-end broaden their vision to the forefront of the public account in 5 years. Welcome to add my wechat ruochuan12 for long-term communication and learning.

This is to learn the overall source code architecture of the launch-Editor source code series (the ninth). Learn the overall architecture of the source code series (what are the must-see JS libraries) : jQuery, underscore, Lodash, Sentry, VUex, AXIos, KOA, Redux. Overall architecture this word seems to be a bit big, let’s say it is the overall structure of the source code, the main is to learn the overall structure of the code, do not go into other is not the main line of the specific function implementation. In this article, you’ll learn the code for the actual repository. The next article should be “Learn the overall framework of Vuex 4 source code, in-depth understanding of its principles and provide/ Inject principles”.

In this paper, the warehouse address: git clone https://github.com/lxchuan12/open-in-editor.git, best way to read in this paper, cloning warehouse do-it-yourself debug, easy to digest.

If someone talks about how to read the source code, you who are reading the article can recommend my source code series of articles, that is really undeserving ah.

I try to write the article so that readers who want to read the source code but don’t know how to read it can understand. I recommend the use of build environment breakpoints debugging source learning, where will not point where, while debugging, rather than hard to see. Is the so-called: teach people and fish than teach people to fish.

By reading this article you will have learned:

  1. How do I solve the problem that this function reports an error
  2. How to debug learning source code
  3. Launch - editor - middleware, launch - editorEqualization principle

1.1 The Source File of the Page Cannot be Found for a Short time

I don’t know if you have encountered such a scene, you opened your own (or your colleagues) development page, but it is difficult to find the corresponding source file in a short time.

At this point you might think it would be nice to have the ability to click the page button to automatically open the corresponding file with the editor.

Vue-devtools does just that, and you may not know it. I don’t think a lot of people know, because it feels like a lot of people don’t use Vue-Devtools very often.

You may be asking, “I don’t use Vue. Does it have a similar function when I use React?” You might also ask, what editors are Supported? Mainstream VSCode, WebStorm, Atom, Sublime, etc. For more information, see this list Supported Editors.

This article is based on learning especially capital launch-Editor source code, in line with know, know why the purpose, explore vue-devtools “open components in the editor” function implementation principle.

1.2 Describe the principle in one sentence

code path/to/file
Copy the code

In a word, the principle: Using child_process in nodejs, a command like code path/to/file is executed, so the corresponding editor opens the corresponding file, and the corresponding editor is found by executing ps x (Window uses Get-Process) in the Process. You can also specify your own editor.

1.3 What can I do if the Component Cannot be opened when I Open the Editor

When you actually use this feature, you might get an error saying you can’t open the file.

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.
Copy the code

This article is written on a Windows PC, the VSCode editor, and a terminal tool used in the Ubuntu subsystem. At the same time, I recommend my article to use ohmyZsh to create efficient terminal command line tools for Windows, Ubuntu and MAC systems.

The solution is simple.

1.3.1 Method 1: Make sure that the editor you are using can be opened with a command in the terminal, as described in this articleVSCodeAs an example

If your command line itself can’t run a command like code to open the editor, you’re going to get an error. You need to inject VSCode into the command line terminal. The injection method is also simple. Someone in my networking group provided screenshots of macs.

VSCode command + shift + p for macs, CTRL + shift + p for Windows. Then enter shell and select Install Code. The diagram below:

So you can open VSCode in your terminal.

If you can open it at the terminal using the command editor, but you still get an error, chances are your editor is not recognized. Then you can set the specified editor through method 2.

1.3.2 Method 2: Specify the editor by specifying the editor in the environment variable

In the root directory of the vue project, which corresponds to vue3-project, add the.env.delelopment file with the contents of EDITOR=code. It is important to note that my VUe-CLI version is 4.5.12, so it seems that vue-CLI 3.5 and above does not support environment variables such as custom EDITOR.

# .env.development
# Of course, I already have code on my command line.
EDITOR=code
Copy the code

Don’t need to specify the corresponding path of editor (c/Users/lxchu AppData/Local/designed/Microsoft VS Code/bin/Code), because of an error. The reason for the error is that I read the source code and tried it. That will be according to the space truncation, become c/Users/lxchu AppData/Local/designed/Microsoft, of course, an error.

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

If you haven’t solved the problem of reporting errors through the above methods. Welcome to leave a message or contact me on wechat ruochuan12. After all, the computer environment is different, it is difficult to ensure that everyone can perform properly, but we know how it works, it is easy to solve the problem.

Next, let’s explore how the “open component in the editor” function works from a source perspective.

2. Vue-devtools Open Component in editor document

Before exploring the principles, let’s take a look at vue-DevTools’ official documentation.

Vuejs/vue – devtools document

Open component in editor

To enable this feature, follow this guide.

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

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

It also details how to use it under 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.
# Automatically guess which editor to open. You can also specify the editor. A more detailed list of supported editors is shown here
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).
If the 'vue-devtools' developer tool has a prompt to click on the component to show the specific path, then you can open it in the editor.
Copy the code

It also explains how to use it in 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.

To find out more, see this guide.

3. Environment preparation

Familiar with my readers, all know that I recommend debugging to see the source code, is called: where not point where. And debugging is generally written in detail, is hoping to help some people know how to see the source code. So I have to build a new warehouse open – in – the editor git clone https://github.com/lxchuan12/open-in-editor.git, study for cloning.

Install the vue – cli

npm install -g @vue/cli
# OR
yarn global add @vue/cli
Copy the code
node -V
# v14.16.0
vue -V 
# @ vue/cli 4.5.12
vue create vue3-project
# vue3, same for vue2.
# Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)
npm install
# OR
yarn install
Copy the code

Let me also explain my vscode version here.

Code - v 1.55.2Copy the code

Vue CLI 3 out of the box and Webpack methods mentioned earlier.

There is a debug button in vue3-project/package.json.

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

Let’s search for ‘launch-editor-middleware’. Generally, there are no files under node_modules. There’s a simple way to do it. Right next to “Excluded files” there is a Settings icon “Use Troubleshooting Settings” and “Ignore files”, click down.

The rest is not verbose. How vscode is set to search for files containing node_modules?

It went vue3 – project/node_modules / @ vue/cli – service/lib/commands/serve. Using the middleware in js.

As shown in the figure below:

Vue-devtools is implemented out of the box with specific source code

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

// vue3-project/node_modules/@vue/cli-service/lib/commands/serve.js
/ / 46
const launchEditorMiddleware = require('launch-editor-middleware')
/ / 192 rows
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`
    )))
    // Omit some code...
}
Copy the code

When I click vue-devtools, I get a request, http://localhost:8080/__open-in-editor? File = SRC/app.vue, it will open the component without accident.

Then let’s look at the implementation of launchEditorMiddleware.

5. launch-editor-middleware

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

The function in the launch-editor-Middleware middleware is to finally call the launch-editor to 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 => Here is passed () => console.log() function
  // So switch to onErrorCallback and assign it to the error callback function
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }

  // If the second argument is a function, assign it to the error callback as well
  // We passed undefined here
  if (typeof srcRoot === 'function') {
    onErrorCallback = srcRoot
    srcRoot = undefined
  }

  // srcRoot is the passed argument, or the directory of the current node process
  srcRoot = srcRoot || process.cwd()

  // Finally return a function, express middleware
  return function launchEditorMiddleware (req, res, next) {
    / / to omit...}}Copy the code

In the previous paragraph, the way to write the switching parameters is common in many sources. This is for the convenience of user invocation of the parameter. There are multiple parameters, but you can pass one or two.

You can put a break point on it. For example here I will interrupt at launch(path.resolve(srcRoot, File), specifiedEditor, onErrorCallback).

// vue3-project/node_modules/launch-editor-middleware/index.js
module.exports = (specifiedEditor, srcRoot, onErrorCallback) = > {
  // Omit the upper part
  return function launchEditorMiddleware (req, res, next) {
    // Parse the file path according to the request
    const { file } = url.parse(req.url, true).query || {}
    // If there is no file path, an error is reported
    if(! file) { res.statusCode =500
      res.end(`launch-editor-middleware: required query param "file" is missing.`)}else {
      // If not, use launch.
      launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
      res.end()
    }
  }
}
Copy the code

6. launch-editor

Follow the breakpoints to the launchEditor function.

// vue3-project/node_modules/launch-editor/index.js
function launchEditor (file, specifiedEditor, onErrorCallback) {
  // Parse the file path, row number and column number
  const parsed = parseFile(file)
  let { fileName } = parsed
  const { lineNumber, columnNumber } = parsed

  // Check whether the file exists.
  if(! fs.existsSync(fileName)) {return
  }
  // So switch to onErrorCallback and assign it to the error callback function
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }
  // Wrap a layer of functions
  onErrorCallback = wrapErrorCallback(onErrorCallback)

  // Guess which editor the current process is running on
  const [editor, ...args] = guessEditor(specifiedEditor)
  if(! editor) { onErrorCallback(fileName,null)
    return
  }
  // It is the first time that...
}
Copy the code

6.1 wrapErrorCallback Callback to the package error function

onErrorCallback = wrapErrorCallback(onErrorCallback)
Copy the code

WrapErrorCallback is returned to a new function. When wrapErrorCallback executes, onErrorCallback(cb) is executed.

I’m sure readers will understand, and I’m going to mention it separately, mainly because this form of wrapping functions is common in many sources.

Could not open app. vue in the editor. The location of the output code.

// 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)
  }
}
Copy the code

6.2 guessEditor guesses which editor is currently in use

This function does four main things:

  1. If the editor is specified, the parser returns.
  2. Find out which editor is running in the current process.macOSLinuxps xThe command

    windowsTo useGet-ProcessThe command

  3. If you can’t find them, use themprocess.env.VISUALorprocess.env.EDITOR. This is why the opening error can specify the editor using an environment variable.

  4. Finally, I did not find it and returned[null], an error will be reported.

const [editor, ...args] = guessEditor(specifiedEditor)
if(! editor) { onErrorCallback(fileName,null)
    return
}
Copy the code
// vue3-project/node_modules/launch-editor/guess.js
const shellQuote = require('shell-quote')
const childProcess = require('child_process')

module.exports = function guessEditor (specifiedEditor) {
  // If an editor is specified, it is not passed in. If you specify a path.
  / / such as c/Users/lxchu AppData/Local/designed/Microsoft VS Code/bin/Code
  / / will be based on space cut into c/Users/lxchu AppData/Local/designed/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 {
    // The code has been deleted
    if (process.platform === 'darwin') {
      const output = childProcess.execSync('ps x').toString()
      / / to omit
    } else if (process.platform === 'win32') {
      const output = childProcess
        .execSync('powershell -Command "Get-Process | Select-Object Path"', {
          stdio: ['pipe'.'pipe'.'ignore']
        })
        .toString()
        / / to omit
    } 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]}Copy the code

Having looked at the guessEditor function, let’s move on to the rest of the launch-editor.

6.3 The rest of the launch-editor

The following code does not need to look closely, when debugging.

// vue3-project/node_modules/launch-editor/index.js
function launchEditor(){
  // Omit the above part...
  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)
  })
}
Copy the code

The main thing in this large section is the following code, using the child process module. Simply put, the child process 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'})}Copy the code

At this point, I’m almost done. The principle is to use nodeJS child_process, execute similar code path/to/file command.

7. To summarize

Here’s a summary: At the beginning of this article, we proposed a “scenario in which the corresponding source file of the page could not be found for a short time”, and provided solutions to the error situation that is easy to encounter. Second, I configured the environment to debug and learned the yyx990803/launch-editor used in vue-devTools.

7.1 Describe the principle in one sentence

So let’s just review the principles from the beginning.

code path/to/file
Copy the code

In a word, the principle: Using child_process in nodejs, a command like code path/to/file is executed, so the corresponding editor opens the corresponding file, and the corresponding editor is found by executing ps x (Window uses Get-Process) in the Process. You can also specify your own editor.

What else can be done in the end.

Check out umijs/launch-editor and React-dev-utils/launcheditor.js. Their code is almost identical.

You can also use Node.js to do some work to improve development efficiency and learn modules such as child_process.

Also do not imprison their own thinking, the front end is imprison in the page, should broaden the vision.

Node.js is a great tool for us front-end people to explore working with files, working with networks, etc.

If the reader finds something wrong or can be improved, or if there is something that is not clear, feel free to comment. In addition, I think it is well written, and it is of some help to you. I can like it, comment on it, and retweet it. It is also a kind of support to me. If you can pay attention to my front public number: “Wakawa View”, it would be better.

about

Hello, I am Ruochuan, wechat search “Ruochuan vision” to follow me, focus on front-end technology sharing, a vision to help front-end broaden their vision to the forefront of the public account in 5 years. Welcome to add my wechat ruochuan12 for long-term communication and learning.

There are mainly the following series of articles: learning source code overall architecture series, annual summary, JS basic series

Refer to the 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