egg-bin dev mechanism

1. Add the script:{start:"egg-bin dev"} statement to the project file package.json, and execute the npm start command to call the start command of the egg-bin package.

 

2. Add the egg-bin.js file to the bin folder under the egg-bin package, and add the bin:{"egg-bin":"bin/egg-bin.js"} statement to the package.json, so that the command line can be executed There is no need to use the node file form when scripting.

#!/usr/bin/env node

'use strict';

const run = require('common-bin').run;

run(require('../lib/program'));

 

3. The "lib/program.js" module is implemented as a single Program through the common-bin mechanism. The program to be executed can be used to add commands. common-bin will parse the command line parameters, get the name of the command being executed, and call the corresponding script file. For example, the dev command will call the "lib/dev_command.js" script and execute it.

'use strict';

const path = require('path');
const BaseProgram = require('common-bin').Program;

// Register the version number and subcommands, such as egg-bin dev to execute the dev_command.js script file
// The current file path in the execution of __dirname
class Program extends BaseProgram {
  constructor() {
    super();
    this.version = require('../package.json').version;
    this.addCommand('dev', path.join(__dirname, 'dev_command.js'));
    this.addCommand('debug', path.join(__dirname, 'debug_command.js'));
    this.addCommand('test', path.join(__dirname, 'test_command.js'));
    if (process.platform === 'win32') {
      this.addCommand('cov', path.join(__dirname, 'test_command.js'));
    } else {
      this.addCommand('cov', path.join(__dirname, 'cov_command.js'));
    }
  }
}

module.exports = Program;

 

4. The "lib/dev_command.js" module, as the script file to be executed when typing dev on the command line, is implemented based on the "lib/command.js" module under the "egg-bin" package. The main meaning of the dev_command module is to inject configuration items into the start-cluster module to be executed. The start-cluster module calls the startCluster method of the egg framework or other egg-based node frameworks to start the service.

'use strict';

const debug = require('debug')('egg-bin:dev');
const Command = require('./command');

class DevCommand extends Command {
  // The parameter cwd is the directory where the current user script is located, and will be passed into egg or other egg-based frameworks as the baseDir parameter when the start-cluster module starts.
  // args is the remaining arguments typed on the command line
  * run(cwd, args) {
    // command line arguments only allow "--debug" or "--inspect"
    const execArgv = args ? args.filter(str => str.indexOf('--debug') === 0 || str.indexOf('--inspect') === 0) : [];

    // Get the egg module path
    const eggPath = this.getFrameworkOrEggPath(cwd);

    // Get the parameters carried when the start-cluster module is executed
    args = yield this.helper.formatArgs(cwd, args, { eggPath });

    // environment variables
    const options = {
      env: Object.assign({}, process.env),
      execArgv,
    };

    options.env.NODE_ENV = options.env.NODE_ENV || 'development';

    debug('%s %j %j, %j', this.helper.serverBin, args, execArgv, options.env.NODE_ENV);

    yield this.helper.checkDeps();

    // this.helper.serverBin is the file path of the start-cluster module
    // this.helper.forkNode(filepath) calls the file whose execution path is filepath, which is provided by the commin-bin package
    yield this.helper.forkNode(this.helper.serverBin, args, options);
  }

  help() {
    return 'local env start';
  }

  // Get the egg module path
  getFrameworkOrEggPath(cwd) {
    return this.utils.getFrameworkOrEggPath(cwd);
  }
}

module.exports = DevCommand;

 

4.1 The command.js module, based on the module of the same name of commin-bin, builds the base class of egg-bin's own command class. The main thing is to change the helper attribute and add the utils attribute at the same time.

'use strict';

const utils = require('egg-utils');
const BaseCommand = require('common-bin').Command;
const helper = require('./helper');

// Create the base class of the Command command, and modify the helper and utils properties on the basis of require('common-bin').Command
class Command extends BaseCommand {
  constructor() {
    super();
    this.helper = Object.assign({}, this.helper, helper);
  }

  run(/* cwd, args */) {
    throw new Error('Must impl this method');
  }

  help() {
    throw new Error('Must impl this method');
  }

  get utils() {
    return utils;
  }
}

module.exports = Command;

 

4.2 helper.js injects tool methods for the command class, the dev command uses the formatArgs method to inject parameters for the startCluster command, and helper.serverBin points to the start-cluster module under the egg-bin package for starting the service .

'use strict';

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const detect = require('detect-port');
const debug = require('debug')('egg-bin');

// default port
exports.defaultPort = 7001;

// The directory where the "start-cluster" module of "egg-bin" is located
exports.serverBin = path.join(__dirname, 'start-cluster');

exports.getTestFiles = () => {
  const files = process.env.TESTS || 'test/**/*.test.js';
  const base = process.cwd();
  return glob.sync(files, {
    cwd: base,
  }).map(file => {
    return path.join(base, file);
  });
};

exports.getTestSetupFile = () => {
  const setupFile = path.join(process.cwd(), 'test/.setup.js');
  if (fs.existsSync(setupFile)) {
    return setupFile;
  }
  return null;
};

exports.formatTestArgs = args => {
  const newArgs = [
    '--timeout', process.env.TEST_TIMEOUT || '30000',
    '--require', require.resolve('thunk-mocha'),
  ];
  if (process.env.TEST_REPORTER) {
    newArgs.push('--reporter');
    newArgs.push(process.env.TEST_REPORTER);
  }

  if (args.indexOf('intelli-espower-loader') !== -1) {
    console.warn('[egg-bin] don\'t need to manually require `intelli-espower-loader` anymore');
  } else {
    // should be require before args
    newArgs.push('--require');
    newArgs.push(require.resolve('intelli-espower-loader'));
  }

  // auto add setup file as the first test file
  const setupFile = exports.getTestSetupFile();
  if (setupFile) {
    newArgs.push(setupFile);
  }

  return newArgs.concat(exports.getTestFiles()).concat(args);
};

// TODO: add egg-dependencies
// const checkDeps = require('egg-dependencies');
exports.checkDeps = function* () {
  return true;
};

// Get the parameters carried when the start-cluster module is executed
exports.formatArgs = function* (cwd, args, options) {
  options = options || {};

  // The parsing form of the parameters set by the start-cluster module. In the current method, the incoming parameter baseDir is cwd, cluster is 1, and eggPath is options.eggPath
  args.push('--baseDir');
  args.push(cwd);
  args.push('--cluster');
  args.push('1');

  if (options.eggPath) {
    args.push(`--eggPath=${options.eggPath}`);
  }

  // auto detect available port
  if (args.indexOf('-p') === -1 && args.indexOf('--port') === -1) {
    debug('detect available port');

    // Through the mechanism of co, the onFulfilled function inside co is passed to the detect function as the callback resolve, and detect is given its parameter realPort, that is, the port
    // When the generator next method is executed, the constant port is also assigned to the exact port
    const port = yield detect(exports.defaultPort);
    
    if (port !== exports.defaultPort) {
      args.push('-p', port);
      console.warn(`[egg-bin] server port ${exports.defaultPort} is in use, now using port ${port}\n`);
    }
    debug(`use available port ${port}`);
  }
  return args;
};

 

5.start-cluster executes the startCluster method of egg or other frameworks based on egg to start the service.

#!/usr/bin/env node

'use strict';

const debug = require('debug')('egg-bin:start-cluster');
const assert = require('assert');
const commander = require('commander');

// Use the commander module to parse the parameters of the process
commander
  .option('--eggPath [eggPath]')
  .option('--baseDir [baseDir]')
  .option('-p, --port [port]')
  .option('--cluster [workers]')
  .allowUnknownOption(true)
  .parse(process.argv);

// user's startup directory
const baseDir = commander.baseDir;
const workers = commander.cluster ? Number(commander.cluster) : 1;
// port
const port = commander.port;
// Path to egg module or other egg-based framework
const customEgg = commander.eggPath;

assert(customEgg, 'eggPath required, missing any egg frameworks?');

const options = {
  baseDir,
  workers,
  port,
  customEgg,
};

debug('eggPath:%s options:%j', customEgg, options);

// Call the startCluster method of egg or other egg-based frameworks to start the service; options set the port, the directory where the user code is located, etc.
require(customEgg).startCluster(options);

 

Notes:

The commander module is used to simply parse command line arguments.

The co module uses the next method of the generator function as the callback function of the return value Promise of the previous execution code, and the next method of the generator function obtains the parameters given by the callback function when the Promise is successful.

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326962291&siteId=291194637