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.