@optique/core 1.0.0-dev.1438 → 1.0.0-dev.1448

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.
package/dist/message.cjs CHANGED
@@ -67,8 +67,8 @@ function message(message$1, ...values$1) {
67
67
  type: "value",
68
68
  value: value$1
69
69
  });
70
- else if (Array.isArray(value$1)) messageTerms.push(...value$1);
71
- else if (typeof value$1 === "object" && value$1 != null && "type" in value$1) messageTerms.push(value$1);
70
+ else if (Array.isArray(value$1)) messageTerms.push(...cloneMessage(value$1));
71
+ else if (typeof value$1 === "object" && value$1 != null && "type" in value$1) messageTerms.push(cloneMessageTerm(value$1));
72
72
  else throw new TypeError(`Invalid value type in message: ${typeof value$1}.`);
73
73
  }
74
74
  return messageTerms;
package/dist/message.js CHANGED
@@ -66,8 +66,8 @@ function message(message$1, ...values$1) {
66
66
  type: "value",
67
67
  value: value$1
68
68
  });
69
- else if (Array.isArray(value$1)) messageTerms.push(...value$1);
70
- else if (typeof value$1 === "object" && value$1 != null && "type" in value$1) messageTerms.push(value$1);
69
+ else if (Array.isArray(value$1)) messageTerms.push(...cloneMessage(value$1));
70
+ else if (typeof value$1 === "object" && value$1 != null && "type" in value$1) messageTerms.push(cloneMessageTerm(value$1));
71
71
  else throw new TypeError(`Invalid value type in message: ${typeof value$1}.`);
72
72
  }
73
73
  return messageTerms;
package/dist/usage.cjs CHANGED
@@ -181,14 +181,24 @@ function formatUsage(programName, usage, options = {}) {
181
181
  * sorting terms for better readability, and ensuring consistent structure
182
182
  * throughout the usage tree.
183
183
  *
184
- * This function performs two main operations:
184
+ * This function performs three main operations:
185
185
  *
186
- * 1. *Flattening*: Recursively processes all usage terms and merges any
186
+ * 1. *Stripping*: Removes degenerate terms that would render as empty or
187
+ * malformed output, such as options with no names, commands with empty
188
+ * names, arguments with empty metavars, and container terms (`optional`,
189
+ * `multiple`, `exclusive`) whose top-level terms array is empty after
190
+ * recursive normalization. Exclusive branches representing valid
191
+ * zero-token alternatives (e.g., `conditional()` default branches or
192
+ * `optional(constant(...))`) and empty-value literals are preserved.
193
+ * Only branches that become empty because all their content was
194
+ * malformed are removed.
195
+ *
196
+ * 2. *Flattening*: Recursively processes all usage terms and merges any
187
197
  * nested exclusive terms into their parent exclusive term to avoid
188
198
  * redundant nesting. For example, an exclusive term containing another
189
199
  * exclusive term will have its nested terms flattened into the parent.
190
200
  *
191
- * 2. *Sorting*: Reorders terms to improve readability by placing:
201
+ * 3. *Sorting*: Reorders terms to improve readability by placing:
192
202
  * - Commands (subcommands) first
193
203
  * - Options and other terms in the middle
194
204
  * - Positional arguments last (including optional/multiple wrappers around
@@ -198,11 +208,12 @@ function formatUsage(programName, usage, options = {}) {
198
208
  * positional arguments and treats them as arguments for sorting purposes.
199
209
  *
200
210
  * @param usage The usage description to normalize.
201
- * @returns A normalized usage description with flattened exclusive terms
202
- * and terms sorted for optimal readability.
211
+ * @returns A normalized usage description with degenerate terms removed,
212
+ * nested exclusive terms flattened, and remaining terms sorted for
213
+ * optimal readability.
203
214
  */
204
215
  function normalizeUsage(usage) {
205
- const terms = usage.map(normalizeUsageTerm);
216
+ const terms = usage.map(normalizeUsageTerm).filter(isNonDegenerateTerm);
206
217
  terms.sort((a, b) => {
207
218
  const aCmd = a.type === "command";
208
219
  const bCmd = b.type === "command";
@@ -229,7 +240,7 @@ function normalizeUsageTerm(term) {
229
240
  if (normalized.length >= 1 && normalized[0].type === "exclusive") {
230
241
  const rest = normalized.slice(1);
231
242
  for (const subUsage of normalized[0].terms) terms.push([...subUsage, ...rest]);
232
- } else terms.push(normalized);
243
+ } else if (normalized.length > 0 || !containsMalformedLeaf(usage)) terms.push(normalized);
233
244
  }
234
245
  return {
235
246
  type: "exclusive",
@@ -237,6 +248,27 @@ function normalizeUsageTerm(term) {
237
248
  };
238
249
  } else return term;
239
250
  }
251
+ function isNonDegenerateTerm(term) {
252
+ if (term.type === "option") return term.names.length > 0;
253
+ if (term.type === "command") return term.name !== "";
254
+ if (term.type === "argument") return term.metavar.length > 0;
255
+ if (term.type === "optional" || term.type === "multiple" || term.type === "exclusive") return term.terms.length > 0;
256
+ return true;
257
+ }
258
+ function containsMalformedLeaf(usage) {
259
+ for (const term of usage) {
260
+ if (term.type === "option" && term.names.length === 0) return true;
261
+ if (term.type === "command" && term.name === "") return true;
262
+ if (term.type === "argument" && term.metavar.length === 0) return true;
263
+ if (term.type === "optional" || term.type === "multiple") {
264
+ if (containsMalformedLeaf(term.terms)) return true;
265
+ }
266
+ if (term.type === "exclusive") {
267
+ for (const branch of term.terms) if (containsMalformedLeaf(branch)) return true;
268
+ }
269
+ }
270
+ return false;
271
+ }
240
272
  /**
241
273
  * Creates a deep clone of a single {@link UsageTerm}. Recursive term
242
274
  * variants (`optional`, `multiple`, `exclusive`) are cloned recursively.
package/dist/usage.d.cts CHANGED
@@ -332,14 +332,24 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
332
332
  * sorting terms for better readability, and ensuring consistent structure
333
333
  * throughout the usage tree.
334
334
  *
335
- * This function performs two main operations:
335
+ * This function performs three main operations:
336
336
  *
337
- * 1. *Flattening*: Recursively processes all usage terms and merges any
337
+ * 1. *Stripping*: Removes degenerate terms that would render as empty or
338
+ * malformed output, such as options with no names, commands with empty
339
+ * names, arguments with empty metavars, and container terms (`optional`,
340
+ * `multiple`, `exclusive`) whose top-level terms array is empty after
341
+ * recursive normalization. Exclusive branches representing valid
342
+ * zero-token alternatives (e.g., `conditional()` default branches or
343
+ * `optional(constant(...))`) and empty-value literals are preserved.
344
+ * Only branches that become empty because all their content was
345
+ * malformed are removed.
346
+ *
347
+ * 2. *Flattening*: Recursively processes all usage terms and merges any
338
348
  * nested exclusive terms into their parent exclusive term to avoid
339
349
  * redundant nesting. For example, an exclusive term containing another
340
350
  * exclusive term will have its nested terms flattened into the parent.
341
351
  *
342
- * 2. *Sorting*: Reorders terms to improve readability by placing:
352
+ * 3. *Sorting*: Reorders terms to improve readability by placing:
343
353
  * - Commands (subcommands) first
344
354
  * - Options and other terms in the middle
345
355
  * - Positional arguments last (including optional/multiple wrappers around
@@ -349,8 +359,9 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
349
359
  * positional arguments and treats them as arguments for sorting purposes.
350
360
  *
351
361
  * @param usage The usage description to normalize.
352
- * @returns A normalized usage description with flattened exclusive terms
353
- * and terms sorted for optimal readability.
362
+ * @returns A normalized usage description with degenerate terms removed,
363
+ * nested exclusive terms flattened, and remaining terms sorted for
364
+ * optimal readability.
354
365
  */
355
366
  declare function normalizeUsage(usage: Usage): Usage;
356
367
  /**
package/dist/usage.d.ts CHANGED
@@ -332,14 +332,24 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
332
332
  * sorting terms for better readability, and ensuring consistent structure
333
333
  * throughout the usage tree.
334
334
  *
335
- * This function performs two main operations:
335
+ * This function performs three main operations:
336
336
  *
337
- * 1. *Flattening*: Recursively processes all usage terms and merges any
337
+ * 1. *Stripping*: Removes degenerate terms that would render as empty or
338
+ * malformed output, such as options with no names, commands with empty
339
+ * names, arguments with empty metavars, and container terms (`optional`,
340
+ * `multiple`, `exclusive`) whose top-level terms array is empty after
341
+ * recursive normalization. Exclusive branches representing valid
342
+ * zero-token alternatives (e.g., `conditional()` default branches or
343
+ * `optional(constant(...))`) and empty-value literals are preserved.
344
+ * Only branches that become empty because all their content was
345
+ * malformed are removed.
346
+ *
347
+ * 2. *Flattening*: Recursively processes all usage terms and merges any
338
348
  * nested exclusive terms into their parent exclusive term to avoid
339
349
  * redundant nesting. For example, an exclusive term containing another
340
350
  * exclusive term will have its nested terms flattened into the parent.
341
351
  *
342
- * 2. *Sorting*: Reorders terms to improve readability by placing:
352
+ * 3. *Sorting*: Reorders terms to improve readability by placing:
343
353
  * - Commands (subcommands) first
344
354
  * - Options and other terms in the middle
345
355
  * - Positional arguments last (including optional/multiple wrappers around
@@ -349,8 +359,9 @@ declare function formatUsage(programName: string, usage: Usage, options?: UsageF
349
359
  * positional arguments and treats them as arguments for sorting purposes.
350
360
  *
351
361
  * @param usage The usage description to normalize.
352
- * @returns A normalized usage description with flattened exclusive terms
353
- * and terms sorted for optimal readability.
362
+ * @returns A normalized usage description with degenerate terms removed,
363
+ * nested exclusive terms flattened, and remaining terms sorted for
364
+ * optimal readability.
354
365
  */
355
366
  declare function normalizeUsage(usage: Usage): Usage;
356
367
  /**
package/dist/usage.js CHANGED
@@ -180,14 +180,24 @@ function formatUsage(programName, usage, options = {}) {
180
180
  * sorting terms for better readability, and ensuring consistent structure
181
181
  * throughout the usage tree.
182
182
  *
183
- * This function performs two main operations:
183
+ * This function performs three main operations:
184
184
  *
185
- * 1. *Flattening*: Recursively processes all usage terms and merges any
185
+ * 1. *Stripping*: Removes degenerate terms that would render as empty or
186
+ * malformed output, such as options with no names, commands with empty
187
+ * names, arguments with empty metavars, and container terms (`optional`,
188
+ * `multiple`, `exclusive`) whose top-level terms array is empty after
189
+ * recursive normalization. Exclusive branches representing valid
190
+ * zero-token alternatives (e.g., `conditional()` default branches or
191
+ * `optional(constant(...))`) and empty-value literals are preserved.
192
+ * Only branches that become empty because all their content was
193
+ * malformed are removed.
194
+ *
195
+ * 2. *Flattening*: Recursively processes all usage terms and merges any
186
196
  * nested exclusive terms into their parent exclusive term to avoid
187
197
  * redundant nesting. For example, an exclusive term containing another
188
198
  * exclusive term will have its nested terms flattened into the parent.
189
199
  *
190
- * 2. *Sorting*: Reorders terms to improve readability by placing:
200
+ * 3. *Sorting*: Reorders terms to improve readability by placing:
191
201
  * - Commands (subcommands) first
192
202
  * - Options and other terms in the middle
193
203
  * - Positional arguments last (including optional/multiple wrappers around
@@ -197,11 +207,12 @@ function formatUsage(programName, usage, options = {}) {
197
207
  * positional arguments and treats them as arguments for sorting purposes.
198
208
  *
199
209
  * @param usage The usage description to normalize.
200
- * @returns A normalized usage description with flattened exclusive terms
201
- * and terms sorted for optimal readability.
210
+ * @returns A normalized usage description with degenerate terms removed,
211
+ * nested exclusive terms flattened, and remaining terms sorted for
212
+ * optimal readability.
202
213
  */
203
214
  function normalizeUsage(usage) {
204
- const terms = usage.map(normalizeUsageTerm);
215
+ const terms = usage.map(normalizeUsageTerm).filter(isNonDegenerateTerm);
205
216
  terms.sort((a, b) => {
206
217
  const aCmd = a.type === "command";
207
218
  const bCmd = b.type === "command";
@@ -228,7 +239,7 @@ function normalizeUsageTerm(term) {
228
239
  if (normalized.length >= 1 && normalized[0].type === "exclusive") {
229
240
  const rest = normalized.slice(1);
230
241
  for (const subUsage of normalized[0].terms) terms.push([...subUsage, ...rest]);
231
- } else terms.push(normalized);
242
+ } else if (normalized.length > 0 || !containsMalformedLeaf(usage)) terms.push(normalized);
232
243
  }
233
244
  return {
234
245
  type: "exclusive",
@@ -236,6 +247,27 @@ function normalizeUsageTerm(term) {
236
247
  };
237
248
  } else return term;
238
249
  }
250
+ function isNonDegenerateTerm(term) {
251
+ if (term.type === "option") return term.names.length > 0;
252
+ if (term.type === "command") return term.name !== "";
253
+ if (term.type === "argument") return term.metavar.length > 0;
254
+ if (term.type === "optional" || term.type === "multiple" || term.type === "exclusive") return term.terms.length > 0;
255
+ return true;
256
+ }
257
+ function containsMalformedLeaf(usage) {
258
+ for (const term of usage) {
259
+ if (term.type === "option" && term.names.length === 0) return true;
260
+ if (term.type === "command" && term.name === "") return true;
261
+ if (term.type === "argument" && term.metavar.length === 0) return true;
262
+ if (term.type === "optional" || term.type === "multiple") {
263
+ if (containsMalformedLeaf(term.terms)) return true;
264
+ }
265
+ if (term.type === "exclusive") {
266
+ for (const branch of term.terms) if (containsMalformedLeaf(branch)) return true;
267
+ }
268
+ }
269
+ return false;
270
+ }
239
271
  /**
240
272
  * Creates a deep clone of a single {@link UsageTerm}. Recursive term
241
273
  * variants (`optional`, `multiple`, `exclusive`) are cloned recursively.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1438+20bfdfd1",
3
+ "version": "1.0.0-dev.1448+eeae0552",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",