CLI: Update to use util.parseArgs (#11245)
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run

---------

Co-authored-by: Guangcong Luo <guangcongluo@gmail.com>
This commit is contained in:
Slayer95 2025-07-17 07:06:26 -05:00 committed by GitHub
parent 38b0d9475e
commit 19d279c9b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 132 additions and 102 deletions

15
build
View File

@ -2,10 +2,12 @@
"use strict";
try {
// technically this was introduced in Node 17, but we'll ask for the most recent LTS with it to be safe
structuredClone({});
// fetch was introduced in Node 18, which is EOL,
// so we'll ask for the most recent "Active LTS" with it to be safe
// https://nodejs.org/en/about/previous-releases
fetch;
} catch (e) {
console.log("We require Node.js version 18 or later; you're using " + process.version);
console.error("We require Node.js version 22 or later; you're using " + process.version);
process.exit(1);
}
@ -22,13 +24,10 @@ function shell(cmd) {
// Check to make sure the most recently added or updated dependency is installed at the correct version
try {
var version = require('esbuild').version.split('.');
if (parseInt(version[1]) < 16) {
throw new Error("esbuild version too old");
}
require.resolve('ts-chacha20');
} catch (e) {
console.log('Installing dependencies...');
shell('npm install');
shell('npm ci');
}
// Make sure config.js exists. If not, copy it over synchronously from

View File

@ -22,8 +22,11 @@
// on the side of caution and run `node build` to ensure we're always running
// with the latest code.
var fs = require('fs');
var util = require('util');
var cli = null;
function build() {
if (process.argv.includes('--skip-build')) return;
if (cli.values['skip-build']) return;
require('child_process').execSync('node build', {stdio: 'inherit', cwd: __dirname});
}
@ -40,65 +43,116 @@ function ensureBuilt() {
}
}
if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
function showHelp() {
console.log('pokemon-showdown start [--skip-build] [PORT]');
console.log('');
console.log(' Starts a PS server on the specified port');
console.log(' (Defaults to the port setting in config/config.js)');
console.log(' (The port setting in config/config.js defaults to 8000)');
console.log('');
console.log('pokemon-showdown generate-team [FORMAT-ID] [RANDOM-SEED]');
console.log('');
console.log(' Generates a random team, and writes it to stdout in packed team format');
console.log(' (Format defaults to the latest Random Battles format)');
console.log('');
console.log('pokemon-showdown validate-team [FORMAT-ID]');
console.log('');
console.log(' Reads a team from stdin, and validates it');
console.log(' If valid: exits with code 0');
console.log(' If invalid: writes errors to stderr, exits with code 1');
console.log('');
console.log('pokemon-showdown simulate-battle');
console.log('');
console.log(' Simulates a battle, taking input to stdin and writing output to stdout');
console.log(' Protocol is documented in ./dist/sim/README.md');
console.log('');
console.log('pokemon-showdown json-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the unpacked JSON to stdout');
console.log('');
console.log('pokemon-showdown pack-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the packed team to stdout');
console.log('');
console.log('pokemon-showdown export-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the exported (human-readable) team to stdout');
console.log('');
console.log('pokemon-showdown help');
console.log('');
console.log(' Displays this reference');
}
try {
fetch;
} catch (e) {
// fetch was introduced in Node 18, which is EOL,
// so we'll ask for the most recent "Active LTS" with it to be safe
// https://nodejs.org/en/about/previous-releases
console.error("We require Node.js version 22 or later; you're using " + process.version);
process.exit(1);
}
var cliOpts = {
help: {
type: 'boolean',
short: 'h',
default: false
},
'skip-build': {
type: 'boolean',
default: false
},
debug: {
type: 'boolean',
short: 'D',
default: false
},
replay: {
type: 'boolean',
short: 'R',
default: false
},
spectate: {
type: 'boolean',
short: 'S',
default: false
}
};
var parserOpts = {
options: cliOpts,
strict: false,
allowPositionals: true,
allowNegative: false
};
cli = util.parseArgs(parserOpts);
if (cli.values['help']) {
showHelp();
process.exit(0);
}
if (!cli.positionals.length || /^[0-9]+$/.test(cli.positionals[0])) {
// Start the server.
//
// The port the server should host on can be passed using the second argument
// when launching with this file the same way app.js normally allows, e.g. to
// The port the server should host on can be passed as an argument, e.g. to
// host on port 9000:
// $ ./pokemon-showdown 9000
build();
require('module')._load('./dist/server/index.js', module, true);
} else {
switch (process.argv[2]) {
switch (cli.positionals[0]) {
case 'help':
case 'h':
case '?':
case '-h':
case '--help':
case '-?':
console.log('pokemon-showdown start [--skip-build] [PORT]');
console.log('');
console.log(' Starts a PS server on the specified port');
console.log(' (Defaults to the port setting in config/config.js)');
console.log(' (The port setting in config/config.js defaults to 8000)');
console.log('');
console.log('pokemon-showdown generate-team [FORMAT-ID [RANDOM-SEED]]');
console.log('');
console.log(' Generates a random team, and writes it to stdout in packed team format');
console.log(' (Format defaults to the latest Random Battles format)');
console.log('');
console.log('pokemon-showdown validate-team [FORMAT-ID]');
console.log('');
console.log(' Reads a team from stdin, and validates it');
console.log(' If valid: exits with code 0');
console.log(' If invalid: writes errors to stderr, exits with code 1');
console.log('');
console.log('pokemon-showdown simulate-battle');
console.log('');
console.log(' Simulates a battle, taking input to stdin and writing output to stdout');
console.log(' Protocol is documented in ./dist/sim/README.md');
console.log('');
console.log('pokemon-showdown json-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the unpacked JSON to stdout');
console.log('');
console.log('pokemon-showdown pack-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the packed team to stdout');
console.log('');
console.log('pokemon-showdown export-team');
console.log('');
console.log(' Reads a team in any format from stdin, writes the exported (human-readable) team to stdout');
console.log('');
console.log('pokemon-showdown help');
console.log('');
console.log(' Displays this reference');
break;
case 'start':
case '--skip-build':
{
process.argv.splice(2, 1);
showHelp();
break;
}
case 'start':
{
build();
require('module')._load('./dist/server/index.js', module, true);
break;
@ -107,8 +161,8 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
{
ensureBuilt();
var Teams = require('./dist/sim/teams.js').Teams;
var seed = process.argv[4] || undefined;
console.log(Teams.pack(Teams.generate(process.argv[3], {seed})));
var seed = cli.positionals[2] || undefined;
console.log(Teams.pack(Teams.generate(cli.positionals[1], {seed})));
}
break;
case 'validate-team':
@ -117,7 +171,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
require('source-map-support/register');
var Teams = require('./dist/sim/teams.js').Teams;
var TeamValidator = require('./dist/sim/team-validator.js').TeamValidator;
var validator = TeamValidator.get(process.argv[3]);
var validator = TeamValidator.get(cli.positionals[1]);
var Streams = require('./dist/lib/streams.js');
var stdin = Streams.stdin();
@ -145,44 +199,10 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
var stdin = Streams.stdin();
var stdout = Streams.stdout();
var args = process.argv.slice(3);
var options = args.flatMap(function (arg) {
if (arg.charAt(0) !== '-') {
if (arg) console.error("Invalid parameter: " + arg);
return [];
} else if (arg.charAt(1) === '-') {
return arg.slice(2);
} else {
return Array.from(arg.slice(1));
}
});
var debug = false;
var replay = false;
var spectate = false;
for (var i = 0; i < options.length; i++) {
switch (options[i]) {
case 'debug': case 'D':
debug = true;
break;
case 'replay': case 'R':
replay = true;
break;
case 'spectate': case 'spectator': case 'S':
replay = true;
spectate = true;
break;
default:
console.error("Invalid option: " + options[i]);
break;
}
}
var battleStream = new BattleTextStream({
noCatch: true,
debug: debug,
replay: spectate ? 'spectator' : replay,
debug: cli.values['debug'],
replay: cli.values['spectate'] ? 'spectator' : cli.values['replay']
});
stdin.pipeTo(battleStream);
battleStream.pipeTo(stdout);
@ -247,7 +267,7 @@ if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
}
break;
default:
console.error('Unrecognized command: ' + process.argv[2]);
console.error('Unrecognized command: ' + cli.positionals[0]);
console.error('Use `pokemon-showdown help` for help');
process.exit(1);
}

View File

@ -50,12 +50,23 @@ try {
}
// NOTE: This file intentionally doesn't use too many modern JavaScript
// features, so that it doesn't crash old versions of Node.js, so we
// can successfully print the "We require Node.js 18+" message.
// can successfully print the "We require Node.js 22+" message.
// Check for version
const nodeVersion = parseInt(process.versions.node);
if (isNaN(nodeVersion) || nodeVersion < 18) {
throw new Error("We require Node.js version 18 or later; you're using " + process.version);
// I've gotten enough reports by people who don't use the launch
// script that this is worth repeating here
try {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
fetch;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
throw new Error("We require Node.js version 22 or later; you're using " + process.version);
}
try {
require.resolve('ts-chacha20');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
throw new Error("Dependencies are unmet; run `npm ci` before launching Pokemon Showdown again.");
}
import { FS, Repl } from '../lib';