Every morning, go back to the corresponding project directory, open the command line tool, and type NPM run XXX. It’s boring to repeat. Especially in the face of the increasing number of projects, I wish I could have a tool to help me manage all projects, with two hands, with one hand to start projects, with the other hand to grasp my char siu bao. emmmm…

Need project source point here ha 😊

Interface Function Description

Project addition: Complete parsing by dragging and dropping the project package.json file into the Application panel (not the.json file? You try)

Project management: the red area shows the added projects, supporting switching/deleting/renaming; The yellow area shows the scripts in package.json. Click to execute them. Green area You can add, delete, or switch multiple command line Windows under this item. The blue area is the command line execution area.

Three wishes at once:

  1. Small terminal client, multi-tab switch, group management by project
  2. Support persistent storage, reopen the application, basic project information can be restored, not lost
  3. Development/packaging can be done with a single click instead of a command line
  4. High level of appearance, level of appearance is justice(No rebuttals accepted!!)

Technology stack

  • Operating environment:node v8+ Electron V4.0 + macOSX V10.13 (Not yet available for Windows, because Node-pty runs incorrectly on Windows all the time. If you’re interested, give me a shotlink)
  • Electron is responsible for packaging the Web as a desktop application
  • React + Redux web development framework
  • Redux-persist applies the data persistence scheme
  • The node-pty + xtem.js solution for constructing shell command line containers on the Web side (vscode’s built-in shell terminal is also based on them)

Development environment setup

Project Catalog overview

Electron – electron – quick – start

The main field of the package.json directory specifies the main renderer file for electron. Everything except this JS file belongs to the renderer process.

“main”: “electron/index.js”

In electron/index.js, configure the files for electron to load the development server files in the development environment and the local files in the build environment.

isDev 
    ? mainWindow.loadURL('http://localhost:3000/index.html')
    : mainWindow.loadFile(path.join(__dirname, '.. /react/build/index.html'))
Copy the code

The react – create – react – app

  • Add the react index.html file to the electron directory and change it in react/config/paths.js

    appHtml: resolveApp(‘.. /electron/index.html’)

  • React scaffolding does not support stylus by default? My heart hurts. Make your own food and clothing. Create-react-app scaffolding hides the webpack configuration in node_modules by default and requires NPM run eject to release it. Find/config/webpack. Config. Js file, referring to the configuration of sass, write again stylus, xx. After this styl file will be stylus – loader, xx. The module. The styl files are as local style processing, Similar to.vue file

    const stylusRegex = /\.(styl)$/; const stylusModuleRegex = /\.module\.(styl)$/; // Add stylus configuration to module {test: stylusRegex,
      exclude: stylusModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 2,
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },
        'stylus-loader'
      ),
      sideEffects: true}, {test: stylusModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 2,
          sourceMap: isEnvProduction && shouldUseSourceMap,
          modules: true,
          getLocalIdent: getCSSModuleLocalIdent,
        },
        'stylus-loader'),},Copy the code
  • Use developer tools. Install the react-devTool.

Start Coding

react

  1. componentDidUpdate

    After the application window is resized, the Web terminal emulator must re-size the parent element. Use this. SetState ({}, callback); The other is handled in componentDidUpdate. The former is particularly convenient and the update behavior is tied to the data source, similar to vue’s vm.$nextTick. The latter is a lot more complicated (you don’t need to handle state globally), and you need to specify an isNew variable to determine whether the DOM update callback is executed.

  2. Passing of a ref attribute reference

    The ref attribute is not part of props, so it is unusual for higher-order components to “bend” forward.

    // Layout.js
    import Main from 'Main.js'
    export default class Layout extends Component {
      constructor (props) {
        super(props)
        this.mainRef = React.createRef()
      }
    
      render () {
        return (
          <Main ref={this.mainRef} />
        )
      }
    }
    
    // Main.js
    class Main extends Component {
      render () {
        const {
          myRef
        } = this.props
    
        return (
          <div ref={myRef}></div>
        )
      }
    }
    
    exportForwardRef ((props, ref) => {// assign the ref reference to name'myRef'Props to achieve the purpose of transmissionreturn( <Main myRef={ref} {... props} /> ) })Copy the code

redux

  1. Asynchronous dispatch action

    Redux-thunk /redux-saga can be used, as the NodeJS environment natively supports synchronous file reading fs.readfilesync, so both of the following methods can be used.

  1. reselect

    Computed attributes similar to vuex

    
    // /store/selectors/project.js
    import { createSelector } from 'reselect'// Calculate dependent values const projectsSelector = state => state.project.projects const activeIdSelector = state => state.project.activeIdexportconst getXtermList = createSelector( projectsSelector, activeIdSelector, (projects, Id) => {const project = projects.find(p => p.id === id) // mustreturn a new "xterms", otherwhiles, it cannot update. Const xterms = (project && project. Xterms)? Const xterms = (project && project. [...project.xterms] : []return xterms
      }
    )
    
    
    // /src/Tab.js
    import { getXtermList } from '/store/selectors/project.js'
    @connect(
      state => ({
        xterms: getXtermList(state),
      })
    )
    class Tabs extends Component {
    
      render () {
    
        <div>
          {
            this.props.xterms.map(() => (
              <div>
                {/* ... */}
              </div>
            ))
          }
        </div>
      }
    }
    export default Tabs
    Copy the code
  2. redux-persist

    • Story – persist also use webStorage (localStorage/sessionStorage), only support ES5 data types, so you need to do to our store data filtering, leaving only the fields of the basic information of the project.
    • The official document is also poor enough and did not say that we need to reform our reducer by ourselves. Finally, Google found treasure in the issue and the data could not be restored after refreshing

node-pty + xterm.js

  1. Node-pty pseudo-terminal is the communication intermediate library between Node and system shell. Xterm.js is responsible for drawing the terminal emulator on the browser side. The Web terminal uses forms to simulate input, and basically has the API capabilities of all forms. It supports automatic code triggering and manual input triggering.

    const os = window.require('os')
    const pty = window.require('node-pty')
    const Terminal = window.require('xterm').Terminal
    
    class Xterm {
    
      constructor () {
    
        this.xterm = null
        this.ptyProcess = null
    
        this.createTerminal()
      }
    
      createTerminal() {const shell = xterm.shell // create a dummy terminal process this.ptyProcess = pty.spawn(shell, [], This.opts) // Create web Terminal emulator this.xterm = new Terminal() this.initEvent()}initEvent() {// The web terminal emulator listens to user input and writes to the system shell this.xterm.on('data', data => {this.ptyprocess. write(data)}) // Node-pty listens to system shell output and writes to web terminal simulator this.ptyProcess.on('data', data => {this.xterm.write(data)})} /** * obtain system information, obtain the corresponding shell terminal */ static getshell () {
        return window.process.env[os.platform() === 'win32' ? 'COMSPEC' : 'bash']}}Copy the code

electron

  1. Nodejs and WebPack module management conflict Webpack continues to use import/require and node module is introducedwindow.require, can escape the compilation of WebPack

  1. The main process debugging

    • A warm restart:

      • The electron-reload plug-in tool can be used to load the. HTML file
      • Since our electron load is a Webpack-dev-server development server, we need to use Nodemon (listening for other files except the React source – app folder) to do the application restart. The Hot restart of the React code is based on its own scaffolding.
      // package.json
      "scripts": {
       "start": "electron ."."watch": "nodemon --watch . --ignore 'app' --exec \"npm start\""."rebuild": "electron-rebuild -f -w node-pty"
      }
      Copy the code
    • Print: Using the electron log command, the printed information is displayed in the console like the node debugging information

  2. Communication between the main process and the renderer

    Electron provides a variety of implementations for communication between main process and Renderer processes. For example, ipcRenderer and ipcMain modules can be used to send messages. In this way, you can simulate system-level actions (such as opening a file directory on the system) by right-clicking the menu

    // react
    const { ipcRenderer } = window.require('electron'// Renderer process sends a request to display the right menu.'show-context-menu'})
    
    // electron
    const {
      app,
      BrowserWindow,
      ipcMain,
      Menu,
      MenuItem
    } = require('electron')
    
    const template = [
      {
        label: 'rename',
        click: this.rename.bind(this)
      },
      {
        label: 'Open file directory', click: This. OpenFileManager. Bind (this)}] / / create right-click menu const menu = menu. BuildFromTemplate (template) / / the main process to monitor the renderer Check process request ipcMain.'show-context-menu', (e, data) = > {const win. = BrowserWindow fromWebContents (e.s ender) / / popup menu menu popup (win)})Copy the code
  3. Native Desktop APP menu

    Make app functions like the peel/Toggle console/refresh app permanent in app menu items

Pack and release the electron- React project

  • Packaging. The tool uses electron Builder to ensure that the system environment must use NoDev8 version. I used V10, ran almost all demo projects on the Internet, and found that there were errors in the packaging process. For three or four days I stoned in despair.

    • The react packaging. File references use the relative path — inpackage.jsonadd“homepage”: “./”. The electron application uses a relative path to load resources using local files, while the web service background used to load resources using an absolute path.

    • Compilation of node native modules

      If the project uses some node native modules (node.js extensions written in C++), they need to be compiled after installation before they can be used. For example, if the project uses Node-pty, it can be compiled in either of the following ways. If it is not compiled, an error will be reported. The first method is automatically executed after NPM install, and the second method needs to be executed manually.

      To ensure your native dependencies are always matched electron version from the description of the electron builder

      • "postinstall": "electron-builder install-app-deps"
      • "rebuild": "electron-rebuild -f -w node-pty"
    • Electron depends on download. Windows and MAC both have a global cache path. If the download is stuck using NPM, try to download the file from the electron download website of Taobao to the corresponding cache directory of the system, and then use NPM install to install the downloaded version number. The cached electron file can then be used. (I? Of course is to move 🍇(harmony) direct download)

    MAC Cache Directory

    Windows Cache Directory

  • Release version

    With Travis and the electron Builder –publish command, git push will automatically package the app with Travis -ci and submit it to github release.

    • Configure Travis CI to associate the code repository with the CI publishing process. Refer to the tutorial
    • Configuration.travis. Yml, the configuration file that packages the published workflow

    • Post release effect

    GitHub has provided the packaged program, welcome to download and use or download the source code to build (currently only support macOS) download experience address

  • The installation program

    Untrusted applications are prompted during installation? Sorry, not enough time for macOS signatures. So you need to allow yourself to run the program. Processing tutorial

The latter

  • The electron+ React project template electron- React – Boilerplate with higher experience and integration
  • If you like it, pleasePoint a praiseorstarThanks for your encouragement

Refer to the credit

  • Electron – React pack basic demo
  • Electron – Builder packs insights