politty 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{arg-registry-BoqVZRFO.d.ts → arg-registry-DDJpsUea.d.cts} +24 -2
  2. package/dist/arg-registry-DDJpsUea.d.cts.map +1 -0
  3. package/dist/{arg-registry-BoqVZRFO.d.cts → arg-registry-DDJpsUea.d.ts} +24 -2
  4. package/dist/arg-registry-DDJpsUea.d.ts.map +1 -0
  5. package/dist/augment.d.cts +1 -1
  6. package/dist/augment.d.ts +1 -1
  7. package/dist/cli.cjs +1 -1
  8. package/dist/cli.js +1 -1
  9. package/dist/completion/index.d.cts +2 -2
  10. package/dist/completion/index.d.ts +2 -2
  11. package/dist/docs/index.cjs +1 -1
  12. package/dist/docs/index.d.cts +1 -1
  13. package/dist/docs/index.d.ts +1 -1
  14. package/dist/docs/index.js +1 -1
  15. package/dist/{index-Csk1VFou.d.ts → index-DKGn3lIl.d.ts} +2 -2
  16. package/dist/{index-Csk1VFou.d.ts.map → index-DKGn3lIl.d.ts.map} +1 -1
  17. package/dist/{index-Ct48_myg.d.cts → index-WyViqW59.d.cts} +2 -2
  18. package/dist/{index-Ct48_myg.d.cts.map → index-WyViqW59.d.cts.map} +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.cts +3 -3
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +1 -1
  23. package/dist/prompt/clack/index.d.cts +1 -1
  24. package/dist/prompt/clack/index.d.ts +1 -1
  25. package/dist/prompt/index.d.cts +1 -1
  26. package/dist/prompt/index.d.ts +1 -1
  27. package/dist/prompt/inquirer/index.d.cts +1 -1
  28. package/dist/prompt/inquirer/index.d.ts +1 -1
  29. package/dist/{runner--Zn4KN9B.js → runner-D43SkHt5.js} +119 -18
  30. package/dist/runner-D43SkHt5.js.map +1 -0
  31. package/dist/{runner-BloFWJEB.cjs → runner-DvFvokV6.cjs} +119 -18
  32. package/dist/runner-DvFvokV6.cjs.map +1 -0
  33. package/package.json +4 -4
  34. package/dist/arg-registry-BoqVZRFO.d.cts.map +0 -1
  35. package/dist/arg-registry-BoqVZRFO.d.ts.map +0 -1
  36. package/dist/runner--Zn4KN9B.js.map +0 -1
  37. package/dist/runner-BloFWJEB.cjs.map +0 -1
@@ -1848,28 +1848,61 @@ const BUILTIN_FLAGS = new Set([
1848
1848
  "--version"
1849
1849
  ]);
1850
1850
  /**
1851
- * Find the first positional argument in argv, properly skipping global flag values.
1852
- * Without globalExtracted, falls back to the first non-flag token.
1853
- */
1854
- function findFirstPositional(argv, globalExtracted) {
1855
- if (!globalExtracted) return argv.find((arg) => !arg.startsWith("-"));
1856
- const lookup = buildGlobalFlagLookup(globalExtracted);
1851
+ * Find the index of the first positional argument in argv, properly skipping
1852
+ * global flag values. Returns -1 when no positional is present.
1853
+ *
1854
+ * Mirrors `scanForSubcommand`'s conservative stop conditions: the scan stops
1855
+ * (returning -1) on a `--` terminator, a builtin flag (`--help`/`--version`),
1856
+ * an unknown long flag, or an unknown/combined short flag. Past such tokens we
1857
+ * can't tell a flag *value* from a positional, so continuing would misclassify
1858
+ * e.g. `--help plugin` or `--unknown value` and wrongly trip plugin dispatch.
1859
+ *
1860
+ * Without globalExtracted, no flag is global, so any leading flag halts the
1861
+ * scan and a positional is only found when it precedes every flag.
1862
+ */
1863
+ function findFirstPositionalIndex(argv, globalExtracted) {
1864
+ const lookup = globalExtracted ? buildGlobalFlagLookup(globalExtracted) : {
1865
+ aliasMap: /* @__PURE__ */ new Map(),
1866
+ booleanFlags: /* @__PURE__ */ new Set(),
1867
+ flagNames: /* @__PURE__ */ new Set(),
1868
+ cliNames: /* @__PURE__ */ new Set(),
1869
+ aliases: /* @__PURE__ */ new Set(),
1870
+ negationMap: /* @__PURE__ */ new Map(),
1871
+ customNegatedFields: /* @__PURE__ */ new Set()
1872
+ };
1857
1873
  for (let i = 0; i < argv.length; i++) {
1858
1874
  const arg = argv[i];
1859
- if (!arg.startsWith("-")) return arg;
1860
- if (arg === "--") return void 0;
1875
+ if (!arg.startsWith("-")) return i;
1876
+ if (arg === "--" || BUILTIN_FLAGS.has(arg)) return -1;
1861
1877
  if (arg.startsWith("--")) {
1862
- const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1863
- if (isGlobal && shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], lookup.booleanFlags)) i++;
1864
- continue;
1878
+ const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1879
+ if (isGlobal) {
1880
+ if (shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], lookup.booleanFlags)) i++;
1881
+ continue;
1882
+ }
1883
+ if (isSuppressedNegation) continue;
1884
+ return -1;
1865
1885
  }
1866
- if (arg.length === 2) {
1867
- const ch = arg[1];
1868
- if (lookup.aliases.has(ch)) {
1869
- if (shouldConsumeValue(arg, lookup.aliasMap.get(ch) ?? ch, false, argv[i + 1], lookup.booleanFlags)) i++;
1886
+ const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
1887
+ if (withoutDash.length === 1) {
1888
+ const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
1889
+ if (lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) {
1890
+ if (shouldConsumeValue(arg, resolvedName, false, argv[i + 1], lookup.booleanFlags)) i++;
1891
+ continue;
1870
1892
  }
1871
1893
  }
1894
+ return -1;
1872
1895
  }
1896
+ return -1;
1897
+ }
1898
+ /**
1899
+ * Find the first positional argument in argv, properly skipping global flag
1900
+ * values. Thin wrapper over {@link findFirstPositionalIndex} — see that
1901
+ * function for the scan and stop conditions. Returns `undefined` when none.
1902
+ */
1903
+ function findFirstPositional(argv, globalExtracted) {
1904
+ const index = findFirstPositionalIndex(argv, globalExtracted);
1905
+ return index >= 0 ? argv[index] : void 0;
1873
1906
  }
1874
1907
 
1875
1908
  //#endregion
@@ -2331,6 +2364,25 @@ async function runMain(command, options = {}) {
2331
2364
  effectiveOptions = rest;
2332
2365
  }
2333
2366
  const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2367
+ if (effectiveOptions.onUnknownSubcommand && !isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2368
+ const knownSubCommands = listSubCommandNamesWithAliases(command);
2369
+ if (knownSubCommands.size > 0) {
2370
+ const positionalIndex = findFirstPositionalIndex(argv, globalExtracted);
2371
+ const name = positionalIndex >= 0 ? argv[positionalIndex] : void 0;
2372
+ if (name && !knownSubCommands.has(name)) {
2373
+ const forwardArgs = argv.slice(positionalIndex + 1);
2374
+ const exitCode = await effectiveOptions.onUnknownSubcommand({
2375
+ commandPath: [],
2376
+ name,
2377
+ args: forwardArgs
2378
+ });
2379
+ if (typeof exitCode === "number") {
2380
+ await flushStandardStreams();
2381
+ return process.exit(exitCode);
2382
+ }
2383
+ }
2384
+ }
2385
+ }
2334
2386
  if (effectiveOptions.setup) try {
2335
2387
  await effectiveOptions.setup({});
2336
2388
  } catch (e) {
@@ -2348,6 +2400,7 @@ async function runMain(command, options = {}) {
2348
2400
  logger: effectiveOptions.logger,
2349
2401
  globalArgs: effectiveOptions.globalArgs,
2350
2402
  prompt: effectiveOptions.prompt,
2403
+ onUnknownSubcommand: effectiveOptions.onUnknownSubcommand,
2351
2404
  _globalExtracted: globalExtracted,
2352
2405
  _globalCleanup: effectiveOptions.cleanup,
2353
2406
  _context: {
@@ -2364,11 +2417,22 @@ async function runMain(command, options = {}) {
2364
2417
  await effectiveOptions.cleanup(cleanupCtx);
2365
2418
  } catch {}
2366
2419
  }
2367
- if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
2368
- if (process.stderr.writableLength > 0) await new Promise((resolve) => process.stderr.once("drain", resolve));
2420
+ await flushStandardStreams();
2369
2421
  process.exit(result.exitCode);
2370
2422
  }
2371
2423
  /**
2424
+ * Flush stdout/stderr before exit to prevent truncated output when piped
2425
+ * (pipe writes are buffered asynchronously, so exiting early loses data).
2426
+ *
2427
+ * We await a zero-byte write's callback rather than a `drain` event: `drain`
2428
+ * only fires after a `write()` returned `false` (backpressure), so buffered
2429
+ * writes that never tripped it would hang. The write callback is ordered after
2430
+ * all pending writes, so it resolves once the buffer is flushed.
2431
+ */
2432
+ async function flushStandardStreams() {
2433
+ await Promise.all([process.stdout, process.stderr].map((stream) => stream.writableLength > 0 ? new Promise((resolve) => stream.write("", () => resolve())) : Promise.resolve()));
2434
+ }
2435
+ /**
2372
2436
  * Internal implementation of command running
2373
2437
  */
2374
2438
  async function runCommandInternal(command, argv, options = {}) {
@@ -2392,6 +2456,43 @@ async function runCommandInternal(command, argv, options = {}) {
2392
2456
  ...options._parsedGlobalArgs,
2393
2457
  ...parseResult.rawGlobalArgs
2394
2458
  };
2459
+ const nestedCommandPath = context.commandPath ?? [];
2460
+ if (options.onUnknownSubcommand && nestedCommandPath.length > 0) {
2461
+ const knownSubCommands = listSubCommandNamesWithAliases(command);
2462
+ if (knownSubCommands.size > 0) {
2463
+ const positionalIndex = findFirstPositionalIndex(argv, options._globalExtracted);
2464
+ const name = positionalIndex >= 0 ? argv[positionalIndex] : void 0;
2465
+ if (name && !knownSubCommands.has(name)) {
2466
+ const forwardArgs = argv.slice(positionalIndex + 1);
2467
+ const exitCode = await options.onUnknownSubcommand({
2468
+ commandPath: nestedCommandPath,
2469
+ name,
2470
+ args: forwardArgs
2471
+ });
2472
+ if (typeof exitCode === "number") {
2473
+ collector?.stop();
2474
+ if (options.handleSignals) {
2475
+ if (options._globalCleanup) try {
2476
+ await options._globalCleanup({ error: void 0 });
2477
+ } catch {}
2478
+ await flushStandardStreams();
2479
+ process.exit(exitCode);
2480
+ }
2481
+ return exitCode === 0 ? {
2482
+ success: true,
2483
+ result: void 0,
2484
+ exitCode: 0,
2485
+ logs: getCurrentLogs()
2486
+ } : {
2487
+ success: false,
2488
+ error: /* @__PURE__ */ new Error(`Plugin "${[...nestedCommandPath, name].join(" ")}" exited with code ${exitCode}`),
2489
+ exitCode,
2490
+ logs: getCurrentLogs()
2491
+ };
2492
+ }
2493
+ }
2494
+ }
2495
+ }
2395
2496
  if (parseResult.helpRequested || parseResult.helpAllRequested) {
2396
2497
  let hasUnknownSubcommand = false;
2397
2498
  const subCmdNames = listSubCommands(command);
@@ -2604,4 +2705,4 @@ function extractAndValidateGlobal(options) {
2604
2705
 
2605
2706
  //#endregion
2606
2707
  export { renderMarkdown as C, styles as D, setColorEnabled as E, symbols as O, renderInline as S, logger as T, DuplicateFieldError as _, parseArgv as a, ReservedAliasError as b, validateCommand as c, validateDuplicateFields as d, validateDuplicateNegations as f, DuplicateAliasError as g, CaseVariantCollisionError as h, formatValidationErrors as i, createDualCaseProxy as k, validateCrossSchemaCollisions as l, validateReservedAliases as m, runMain as n, formatCommandValidationErrors as o, validatePositionalConfig as p, runner_exports as r, validateCaseVariantCollisions as s, runCommand as t, validateDuplicateAliases as u, DuplicateNegationError as v, isColorEnabled as w, generateHelp as x, PositionalConfigError as y };
2607
- //# sourceMappingURL=runner--Zn4KN9B.js.map
2708
+ //# sourceMappingURL=runner-D43SkHt5.js.map