From 16746c99c959d795b62eec11d33ef871f4b18726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Tue, 18 Jul 2023 19:10:33 +0200 Subject: [PATCH] refactor `run` to a es class --- lib/node-pre-gyp.js | 414 +++++++++++++++++++++----------------------- 1 file changed, 201 insertions(+), 213 deletions(-) diff --git a/lib/node-pre-gyp.js b/lib/node-pre-gyp.js index dc18e749e..8b1d16916 100644 --- a/lib/node-pre-gyp.js +++ b/lib/node-pre-gyp.js @@ -1,20 +1,14 @@ 'use strict'; -/** - * Module exports. - */ - -module.exports = exports; - /** * Module dependencies. */ // load mocking control function for accessing s3 via https. the function is a noop always returning // false if not mocking. -exports.mockS3Http = require('./util/s3_setup').get_mockS3Http(); -exports.mockS3Http('on'); -const mocking = exports.mockS3Http('get'); +const mockS3Http = require('./util/s3_setup.js').get_mockS3Http(); +mockS3Http('on'); +const mocking = mockS3Http('get'); const fs = require('fs'); @@ -24,8 +18,7 @@ const log = require('npmlog'); log.disableProgress(); const napi = require('./util/napi.js'); -const EE = require('events').EventEmitter; -const inherits = require('util').inherits; +const EE = require('events'); const cli_commands = [ 'clean', 'install', @@ -50,14 +43,6 @@ if (mocking) { log.warn(`mocking s3 to ${process.env.node_pre_gyp_mock_s3}`); } -// this is a getter to avoid circular reference warnings with node v14. -Object.defineProperty(exports, 'find', { - get: function() { - return require('./pre-binding').find; - }, - enumerable: true -}); - // in the following, "my_module" is using node-pre-gyp to // prebuild and install pre-built binaries. "main_module" // is using "my_module". @@ -75,52 +60,218 @@ Object.defineProperty(exports, 'find', { // // that's why "find()" must pass the path to package.json. // -function Run({ package_json_path = './package.json', argv }) { - this.package_json_path = package_json_path; - this.commands = {}; - - const self = this; - cli_commands.forEach((command) => { - self.commands[command] = function(argvx, callback) { - log.verbose('command', command, argvx); - return require('./' + command)(self, argvx, callback); - }; - }); - - this.parseArgv(argv); - - // this is set to true after the binary.host property was set to - // either staging_host or production_host. - this.binaryHostSet = false; +class Run extends EE { + constructor({ package_json_path = './package.json', argv }) { + super(); + + this.package_json_path = package_json_path; + this.commands = {}; + + cli_commands.forEach((command) => { + this.commands[command] = (argvx, callback) => { + log.verbose('command', command, argvx); + return require('./' + command)(this, argvx, callback); + }; + }); + + this.parseArgv(argv); + + // this is set to true after the binary.host property was set to + // either staging_host or production_host. + this.binaryHostSet = false; + } + + /** + * Parses the given argv array and sets the 'opts', 'argv', + * 'command', and 'package_json' properties. + */ + parseArgv(argv) { + this.opts = nopt(this.configDefs, this.shorthands, argv); + this.argv = this.opts.argv.remain.slice(); + const commands = this.todo = []; + + // create a copy of the argv array with aliases mapped + argv = this.argv.map((arg) => { + // is this an alias? + if (arg in this.aliases) { + arg = this.aliases[arg]; + } + return arg; + }); + + // process the mapped args into "command" objects ("name" and "args" props) + argv.slice().forEach((arg) => { + if (arg in this.commands) { + const args = argv.splice(0, argv.indexOf(arg)); + argv.shift(); + if (commands.length > 0) { + commands[commands.length - 1].args = args; + } + commands.push({ name: arg, args: [] }); + } + }); + + if (commands.length > 0) { + commands[commands.length - 1].args = argv.splice(0); + } + + // if a directory was specified package.json is assumed to be relative + // to it. + let package_json_path = this.package_json_path; + if (this.opts.directory) { + package_json_path = path.join(this.opts.directory, package_json_path); + } + + this.package_json = JSON.parse(fs.readFileSync(package_json_path, 'utf8')); + + // expand commands entries for multiple napi builds + this.todo = napi.expand_commands(this.package_json, this.opts, commands); + + // support for inheriting config env variables from npm + const npm_config_prefix = 'npm_config_'; + Object.keys(process.env).forEach((name) => { + if (name.indexOf(npm_config_prefix) !== 0) return; + const val = process.env[name]; + if (name === npm_config_prefix + 'loglevel') { + log.level = val; + } else { + // add the user-defined options to the config + name = name.substring(npm_config_prefix.length); + // avoid npm argv clobber already present args + // which avoids problem of 'npm test' calling + // script that runs unique npm install commands + if (name === 'argv') { + if (this.opts.argv && + this.opts.argv.remain && + this.opts.argv.remain.length) { + // do nothing + } else { + this.opts[name] = val; + } + } else { + this.opts[name] = val; + } + } + }); + + if (this.opts.loglevel) { + log.level = this.opts.loglevel; + } + log.resume(); + } + + /** + * allow the binary.host property to be set at execution time. + * + * for this to take effect requires all the following to be true. + * - binary is a property in package.json + * - binary.host is falsey + * - binary.staging_host is not empty + * - binary.production_host is not empty + * + * if any of the previous checks fail then the function returns an empty string + * and makes no changes to package.json's binary property. + * + * + * if command is "publish" then the default is set to "binary.staging_host" + * if command is not "publish" the the default is set to "binary.production_host" + * + * if the command-line option '--s3_host' is set to "staging" or "production" then + * "binary.host" is set to the specified "staging_host" or "production_host". if + * '--s3_host' is any other value an exception is thrown. + * + * if '--s3_host' is not present then "binary.host" is set to the default as above. + * + * this strategy was chosen so that any command other than "publish" or "unpublish" uses "production" + * as the default without requiring any command-line options but that "publish" and "unpublish" require + * '--s3_host production_host' to be specified in order to *really* publish (or unpublish). publishing + * to staging can be done freely without worrying about disturbing any production releases. + */ + setBinaryHostProperty(command) { + if (this.binaryHostSet) { + return this.package_json.binary.host; + } + const p = this.package_json; + // don't set anything if host is present. it must be left blank to trigger this. + if (!p || !p.binary || p.binary.host) { + return ''; + } + // and both staging and production must be present. errors will be reported later. + if (!p.binary.staging_host || !p.binary.production_host) { + return ''; + } + let target = 'production_host'; + if (command === 'publish' || command === 'unpublish') { + target = 'staging_host'; + } + // the environment variable has priority over the default or the command line. if + // either the env var or the command line option are invalid throw an error. + const npg_s3_host = process.env.node_pre_gyp_s3_host; + if (npg_s3_host === 'staging' || npg_s3_host === 'production') { + target = `${npg_s3_host}_host`; + } else if (this.opts['s3_host'] === 'staging' || this.opts['s3_host'] === 'production') { + target = `${this.opts['s3_host']}_host`; + } else if (this.opts['s3_host'] || npg_s3_host) { + throw new Error(`invalid s3_host ${this.opts['s3_host'] || npg_s3_host}`); + } + + p.binary.host = p.binary[target]; + this.binaryHostSet = true; + + return p.binary.host; + } + + /** + * Returns the usage instructions for node-pre-gyp. + */ + usage() { + const str = [ + '', + ' Usage: node-pre-gyp [options]', + '', + ' where is one of:', + cli_commands.map((c) => { + return ' - ' + c + ' - ' + require('./' + c).usage; + }).join('\n'), + '', + 'node-pre-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'), + 'node@' + process.versions.node + ].join('\n'); + return str; + } + + /** + * Version number getter. + */ + get version() { + return this.package.version; + } } -inherits(Run, EE); -exports.Run = Run; -const proto = Run.prototype; /** * Export the contents of the package.json. */ -proto.package = require('../package.json'); +Run.prototype.package = require('../package.json'); /** * nopt configuration definitions */ -proto.configDefs = { +Run.prototype.configDefs = { help: Boolean, // everywhere arch: String, // 'configure' debug: Boolean, // 'build' directory: String, // bin proxy: String, // 'install' - loglevel: String // everywhere + loglevel: String // everywhere }; /** * nopt shorthands */ -proto.shorthands = { +Run.prototype.shorthands = { release: '--no-debug', C: '--directory', debug: '--debug', @@ -134,176 +285,13 @@ proto.shorthands = { * expose the command aliases for the bin file to use. */ -proto.aliases = aliases; - -/** - * Parses the given argv array and sets the 'opts', 'argv', - * 'command', and 'package_json' properties. - */ - -proto.parseArgv = function parseOpts(argv) { - this.opts = nopt(this.configDefs, this.shorthands, argv); - this.argv = this.opts.argv.remain.slice(); - const commands = this.todo = []; - - // create a copy of the argv array with aliases mapped - argv = this.argv.map((arg) => { - // is this an alias? - if (arg in this.aliases) { - arg = this.aliases[arg]; - } - return arg; - }); - - // process the mapped args into "command" objects ("name" and "args" props) - argv.slice().forEach((arg) => { - if (arg in this.commands) { - const args = argv.splice(0, argv.indexOf(arg)); - argv.shift(); - if (commands.length > 0) { - commands[commands.length - 1].args = args; - } - commands.push({ name: arg, args: [] }); - } - }); - if (commands.length > 0) { - commands[commands.length - 1].args = argv.splice(0); - } - - - // if a directory was specified package.json is assumed to be relative - // to it. - let package_json_path = this.package_json_path; - if (this.opts.directory) { - package_json_path = path.join(this.opts.directory, package_json_path); - } - - this.package_json = JSON.parse(fs.readFileSync(package_json_path)); - - // expand commands entries for multiple napi builds - this.todo = napi.expand_commands(this.package_json, this.opts, commands); - - // support for inheriting config env variables from npm - const npm_config_prefix = 'npm_config_'; - Object.keys(process.env).forEach((name) => { - if (name.indexOf(npm_config_prefix) !== 0) return; - const val = process.env[name]; - if (name === npm_config_prefix + 'loglevel') { - log.level = val; - } else { - // add the user-defined options to the config - name = name.substring(npm_config_prefix.length); - // avoid npm argv clobber already present args - // which avoids problem of 'npm test' calling - // script that runs unique npm install commands - if (name === 'argv') { - if (this.opts.argv && - this.opts.argv.remain && - this.opts.argv.remain.length) { - // do nothing - } else { - this.opts[name] = val; - } - } else { - this.opts[name] = val; - } - } - }); - - if (this.opts.loglevel) { - log.level = this.opts.loglevel; - } - log.resume(); -}; +Run.prototype.aliases = aliases; -/** - * allow the binary.host property to be set at execution time. - * - * for this to take effect requires all the following to be true. - * - binary is a property in package.json - * - binary.host is falsey - * - binary.staging_host is not empty - * - binary.production_host is not empty - * - * if any of the previous checks fail then the function returns an empty string - * and makes no changes to package.json's binary property. - * - * - * if command is "publish" then the default is set to "binary.staging_host" - * if command is not "publish" the the default is set to "binary.production_host" - * - * if the command-line option '--s3_host' is set to "staging" or "production" then - * "binary.host" is set to the specified "staging_host" or "production_host". if - * '--s3_host' is any other value an exception is thrown. - * - * if '--s3_host' is not present then "binary.host" is set to the default as above. - * - * this strategy was chosen so that any command other than "publish" or "unpublish" uses "production" - * as the default without requiring any command-line options but that "publish" and "unpublish" require - * '--s3_host production_host' to be specified in order to *really* publish (or unpublish). publishing - * to staging can be done freely without worrying about disturbing any production releases. - */ -proto.setBinaryHostProperty = function(command) { - if (this.binaryHostSet) { - return this.package_json.binary.host; - } - const p = this.package_json; - // don't set anything if host is present. it must be left blank to trigger this. - if (!p || !p.binary || p.binary.host) { - return ''; - } - // and both staging and production must be present. errors will be reported later. - if (!p.binary.staging_host || !p.binary.production_host) { - return ''; - } - let target = 'production_host'; - if (command === 'publish' || command === 'unpublish') { - target = 'staging_host'; - } - // the environment variable has priority over the default or the command line. if - // either the env var or the command line option are invalid throw an error. - const npg_s3_host = process.env.node_pre_gyp_s3_host; - if (npg_s3_host === 'staging' || npg_s3_host === 'production') { - target = `${npg_s3_host}_host`; - } else if (this.opts['s3_host'] === 'staging' || this.opts['s3_host'] === 'production') { - target = `${this.opts['s3_host']}_host`; - } else if (this.opts['s3_host'] || npg_s3_host) { - throw new Error(`invalid s3_host ${this.opts['s3_host'] || npg_s3_host}`); +module.exports = { + Run, + mockS3Http, + // this is a getter to avoid circular reference warnings with node v14. + get find() { + return require('./pre-binding').find; } - - p.binary.host = p.binary[target]; - this.binaryHostSet = true; - - return p.binary.host; }; - -/** - * Returns the usage instructions for node-pre-gyp. - */ - -proto.usage = function usage() { - const str = [ - '', - ' Usage: node-pre-gyp [options]', - '', - ' where is one of:', - cli_commands.map((c) => { - return ' - ' + c + ' - ' + require('./' + c).usage; - }).join('\n'), - '', - 'node-pre-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'), - 'node@' + process.versions.node - ].join('\n'); - return str; -}; - -/** - * Version number getter. - */ - -Object.defineProperty(proto, 'version', { - get: function() { - return this.package.version; - }, - enumerable: true -});