From 2c669c1df7af67442385f83cf80bd9bf503c5797 Mon Sep 17 00:00:00 2001 From: Jan Vennemann Date: Mon, 23 Feb 2026 22:29:55 +0100 Subject: [PATCH 1/2] feat: add `serve` command support Register the new `serve` SDK command and wire up platform-specific config handling so it works like `build`. Adds positional platform shorthand (e.g. `ti serve ios`) by preferring Commander's positional args over the manual trailing-args logic. Bumps version to 8.2.0. --- package.json | 2 +- src/cli.js | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 637f1761..6a713929 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "titanium", - "version": "8.1.5", + "version": "8.2.0", "author": "TiDev, Inc. ", "description": "Command line interface for building Titanium SDK apps", "type": "module", diff --git a/src/cli.js b/src/cli.js index cb793a7d..7368f42f 100644 --- a/src/cli.js +++ b/src/cli.js @@ -36,7 +36,8 @@ const sdkCommands = { build: 'builds a project', clean: 'removes previous build directories', create: 'creates a new project', - project: 'get and set tiapp.xml settings' + project: 'get and set tiapp.xml settings', + serve: 'serves a project through the Titanium Vite runtime', }; /** @@ -416,12 +417,19 @@ export class CLI { const cmd = args.pop(); args.pop(); // discard argv - // remove any trailing undefined args - while (args.length && args[args.length - 1] === undefined) { - args.pop(); + // Commander keeps excess/positional args on the command context. + // Prefer that source so shorthand invocations like `ti serve ios` work + // even when commands do not declare explicit positional arguments. + const positionalArgs = Array.isArray(cmd?.args) ? cmd.args.slice() : []; + if (positionalArgs.length) { + this.argv._ = positionalArgs; + } else { + // Fallback for command handlers that pass positional args directly. + while (args.length && args[args.length - 1] === undefined) { + args.pop(); + } + this.argv._ = args; } - - this.argv._ = args; this.applyArgv(cmd); if (!this.ready) { @@ -621,20 +629,19 @@ export class CLI { } /** - * If the current command is the "build" command, this function will check - * if the "build" command has a `--platform` option (which is should), then - * prompt for the platform is not explicitly passed in. + * If the current command supports platform-specific config, this function + * checks for the `--platform` option and prompts when missing. * - * Finally, the platform specific options and flags are added to the - * Commander.js "build" command context so that the second parse will - * pick up the newly defined options/flags. + * Finally, the platform specific options and flags are added to the command + * context so that the second parse picks up the newly defined options/flags. * * @returns {Promise} * @access private */ async initBuildPlatform() { const cmdName = this.command.name(); - if (cmdName !== 'build') { + // Commands with build-style platform branches. + if (cmdName !== 'build' && cmdName !== 'serve') { return; } @@ -643,6 +650,16 @@ export class CLI { return; } + // Support shorthand positional platform syntax, e.g. `ti serve ios`. + const positionalPlatform = this.argv._[0]; + if (!this.argv.platform && typeof positionalPlatform === 'string' && platformOption.values.includes(positionalPlatform)) { + this.debugLogger.trace(`Converting positional platform shortcut "${positionalPlatform}" to --platform`); + this.argv.platform = positionalPlatform; + if (this.argv._[0] === positionalPlatform) { + this.argv._.shift(); + } + } + // when specifying `--platform ios`, the SDK's option callback converts // it to `iphone`, however the platform config uses `ios` and we must // convert it back From 2ba5e837fc6301ca5644bbfaf429eb0e56df8ed3 Mon Sep 17 00:00:00 2001 From: Jan Vennemann Date: Mon, 23 Feb 2026 23:52:42 +0100 Subject: [PATCH 2/2] refactor: use Commander's .argument() for positional platform parsing Replace custom positional platform detection with Commander's native .argument('[platform]') API. This lets Commander handle the shorthand `ti build ios` / `ti serve ios` syntax natively instead of manually checking argv._[0] after parsing. --- src/cli.js | 17 ++++++++--------- test/commands/ti-build.test.js | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/cli.js b/src/cli.js index 7368f42f..39557e33 100644 --- a/src/cli.js +++ b/src/cli.js @@ -417,9 +417,7 @@ export class CLI { const cmd = args.pop(); args.pop(); // discard argv - // Commander keeps excess/positional args on the command context. - // Prefer that source so shorthand invocations like `ti serve ios` work - // even when commands do not declare explicit positional arguments. + // Commander keeps declared and excess positional args on the command. const positionalArgs = Array.isArray(cmd?.args) ? cmd.args.slice() : []; if (positionalArgs.length) { this.argv._ = positionalArgs; @@ -651,13 +649,12 @@ export class CLI { } // Support shorthand positional platform syntax, e.g. `ti serve ios`. - const positionalPlatform = this.argv._[0]; - if (!this.argv.platform && typeof positionalPlatform === 'string' && platformOption.values.includes(positionalPlatform)) { - this.debugLogger.trace(`Converting positional platform shortcut "${positionalPlatform}" to --platform`); + // Commander parses this into processedArgs via the [platform] argument + // declared in loadCommand(). + const positionalPlatform = this.command.processedArgs?.[0]; + if (!this.argv.platform && positionalPlatform && platformOption.values.includes(positionalPlatform)) { + this.debugLogger.trace(`Converting positional platform argument "${positionalPlatform}" to --platform`); this.argv.platform = positionalPlatform; - if (this.argv._[0] === positionalPlatform) { - this.argv._.shift(); - } } // when specifying `--platform ios`, the SDK's option callback converts @@ -1000,6 +997,8 @@ export class CLI { this.command.createHelp = () => { return Object.assign(new TiHelp(this, conf.platforms), this.command.configureHelp()); }; + + cmd.argument('[platform]', 'target platform'); } applyCommandConfig(this, cmdName, cmd, conf); diff --git a/test/commands/ti-build.test.js b/test/commands/ti-build.test.js index f1148598..e4e67135 100644 --- a/test/commands/ti-build.test.js +++ b/test/commands/ti-build.test.js @@ -9,7 +9,7 @@ describe('ti build', () => { const output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium build \[options\]/); + assert.match(output, /Usage: titanium build \[options\] \[platform\]/); assert.match(output, /Builds an existing app or module project./); assert.match(output, /Build Options:/); assert.match(output, /Global Options:/);