@gunshi/plugin-i18n 0.37.3 → 1.0.0-beta.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.
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Args, Awaitable, Command, CommandContext, DefaultGunshiParams, ExtendContext, ExtractArgs, ExtractExtensions, GunshiParams, GunshiParamsConstraint, PluginWithExtension, Prettify } from "@gunshi/plugin";
2
2
 
3
- //#region ../../node_modules/.pnpm/args-tokens@0.29.0/node_modules/args-tokens/lib/resolver.d.ts
3
+ //#region ../../node_modules/.pnpm/args-tokens@1.0.0-beta.1/node_modules/args-tokens/lib/resolver.d.ts
4
4
 
5
5
  //#region src/resolver.d.ts
6
6
  /**
@@ -47,19 +47,25 @@ interface ArgSchema {
47
47
  /**
48
48
  * Type of the argument value.
49
49
  *
50
- * - `'string'`: Text value (default if not specified)
51
- * - `'boolean'`: `true`/`false` flag (can be negatable with `--no-` prefix)
50
+ * - `'string'`: Text value
51
+ * - `'boolean'`: `true`/`false` flag (can be negatable with `--no-` prefix). `--flag=true` and
52
+ * `--flag=false` set the value explicitly; any other value after `=` is a type error
52
53
  * - `'number'`: Numeric value (parsed as integer or float)
53
- * - `'enum'`: One of predefined string values (requires `choices` property)
54
+ * - `'enum'`: One of predefined string values (requires `choices` property, unless the argument
55
+ * has a `parse` function)
54
56
  * - `'positional'`: Non-option argument by position
55
57
  * - `'custom'`: Custom parsing with user-defined `parse` function
56
58
  *
59
+ * Any other `type`, or no `type`, is a mistake that only untyped code can make: if the argument
60
+ * has no `parse` function, {@link resolveArgs} and `parse()` throw an `Error`, whether or not the
61
+ * argument is given.
62
+ *
57
63
  * @example
58
64
  * Different argument types:
59
65
  * ```ts
60
66
  * {
61
67
  * name: { type: 'string' }, // --name value
62
- * verbose: { type: 'boolean' }, // --verbose or --no-verbose
68
+ * verbose: { type: 'boolean' }, // --verbose (--no-verbose needs negatable: true)
63
69
  * port: { type: 'number' }, // --port 3000
64
70
  * level: { type: 'enum', choices: ['debug', 'info'] },
65
71
  * file: { type: 'positional' }, // first positional arg
@@ -134,14 +140,32 @@ interface ArgSchema {
134
140
  /**
135
141
  * Marks the argument as required.
136
142
  *
137
- * When `true`, the argument must be provided by the user.
138
- * If missing, an `ArgResolveError` with type 'required' will be thrown.
143
+ * When `true`, the argument must be provided by the user. If it is missing, the error is an
144
+ * `ArgResolveError` with type 'required' and the code `err:arg:required-option`
145
+ * ({@link ArgsValidationErrorKeys}.requiredOption), or `err:arg:required-positional`
146
+ * ({@link ArgsValidationErrorKeys}.requiredPositional) for a positional argument. Its
147
+ * `values` has the `displayName` as in the message, such as `'--name'` for an option
148
+ * (`'--name' or '-n'` with the short name `n`) and `'file'` for a positional argument, and
149
+ * the `name`, which is the schema key.
150
+ * An option that is given without a value is reported as `err:arg:missing-value`
151
+ * ({@link ArgsValidationErrorKeys}.missingValue) instead, because the option itself was given.
152
+ * An explicit empty value, such as `--name=` or `-n ''`, is still reported as required, but the
153
+ * option counts as given: it is `true` in `explicit` and takes part in conflicts, as any other
154
+ * given option does.
139
155
  *
140
- * For single-value positional arguments, omitting `required` keeps the argument
141
- * required for compatibility. Set `required: false` to make a positional argument
142
- * optional. Optional positional arguments leave enough input values for later
156
+ * For single-value positional arguments, omitting `required` keeps the argument required for
157
+ * compatibility, unless it has a `default`, which makes it optional. A `multiple` positional
158
+ * argument is optional unless `required: true` is set. Set `required: false` to make a positional
159
+ * argument explicitly optional. Optional positional arguments leave enough input values for later
143
160
  * required positional arguments before consuming a value.
144
161
  *
162
+ * In the type of the values, such as {@link ArgValues}, a single-value positional argument whose
163
+ * `required` may be `false`, such as one of type `boolean`, is optional, as it may be missing,
164
+ * unless it has a `default`. An optional `required` of type `boolean`, as declared here, says
165
+ * nothing, and keeps it required. TypeScript widens a `required: true` to `boolean` in a schema
166
+ * written apart from the call, such as `{ type: 'positional' as const, required: true }`, which
167
+ * types the argument as optional: write such a schema `as const`.
168
+ *
145
169
  * @example
146
170
  * Required arguments:
147
171
  * ```ts
@@ -162,8 +186,9 @@ interface ArgSchema {
162
186
  /**
163
187
  * Allows the argument to accept multiple values.
164
188
  *
165
- * When `true`, the resolved value becomes an array.
166
- * For options: can be specified multiple times (--tag foo --tag bar)
189
+ * When `true`, the resolved value becomes an array. When the argument is not given and has no
190
+ * `default`, its value is `undefined`, not an empty array.
191
+ * For options: can be specified multiple times (--tags foo --tags bar)
167
192
  * For positional: collects remaining positional arguments after preserving values for
168
193
  * later required positional arguments.
169
194
  *
@@ -195,6 +220,12 @@ interface ArgSchema {
195
220
  *
196
221
  * Only applicable to `type: 'boolean'` arguments.
197
222
  *
223
+ * The negated name is always `no-` followed by the full option name. An option named
224
+ * `no-cache` is negated by `--no-no-cache`, and `--no-cache` sets it to `true`.
225
+ *
226
+ * The negated form does not take a value. `--no-flag=<value>` is reported as
227
+ * `err:arg:unexpected-value` ({@link ArgsValidationErrorKeys}.unexpectedValue).
228
+ *
198
229
  * @example
199
230
  * Negatable boolean:
200
231
  * ```ts
@@ -213,8 +244,17 @@ interface ArgSchema {
213
244
  /**
214
245
  * Array of allowed string values for enum-type arguments.
215
246
  *
216
- * Required when `type: 'enum'`. The argument value must be one of these choices,
217
- * otherwise an `ArgResolveError` with type 'type' will be thrown.
247
+ * Required when `type: 'enum'`, unless the argument has a `parse` function: if `choices` is
248
+ * missing or not an array, {@link resolveArgs} and `parse()` throw a `TypeError`, whether or not
249
+ * the argument is given. With a `parse` function and no `choices`, the value is passed to the
250
+ * function as for a `string` argument with a `parse` function.
251
+ *
252
+ * The argument value must be one of these choices, otherwise the error is an `ArgResolveError`
253
+ * with type 'type' and the code `err:arg:invalid-choice`
254
+ * ({@link ArgsValidationErrorKeys}.invalidChoice).
255
+ *
256
+ * The value is checked before `parse` is called, so a `parse` function receives only one of
257
+ * these choices and can change it, for example to upper case.
218
258
  *
219
259
  * Supports both mutable arrays and readonly arrays for type safety.
220
260
  *
@@ -238,18 +278,37 @@ interface ArgSchema {
238
278
  */
239
279
  choices?: string[] | readonly string[];
240
280
  /**
241
- * Default value used when the argument is not provided.
281
+ * Default value used when the argument is not provided. An option that is given without a value,
282
+ * or with a value that is rejected, also gets its default, along with the error.
242
283
  *
243
284
  * The type must match the argument's `type` property:
244
285
  * - `string` type: string default
245
286
  * - `boolean` type: boolean default
246
287
  * - `number` type: number default
247
- * - `enum` type: must be one of the `choices` values
288
+ * - `enum` type: one of the `choices` values, or a value that a `parse` of your own returns
248
289
  * - `positional`/`custom` type: string, boolean, or number default
249
290
  *
250
- * For single-value positional arguments, the default is used when the positional
251
- * value is missing or when the value is preserved for later required positional
252
- * arguments, unless `required: true` is set.
291
+ * The default is used as is and never goes through `parse`, including when an option is given
292
+ * without a value. An explicit empty value, such as `--name=` or `-n ''`, is a value, not a
293
+ * missing one: a `string` option without `parse` gets `''` instead of the default, unless it is
294
+ * `required`. What `parse` returns is a value too, even `undefined` or `null`, and the default
295
+ * does not replace it.
296
+ *
297
+ * The value of a `multiple` argument is an array, so its default becomes the only element of the
298
+ * array: `default: 'latest'` gives `['latest']`.
299
+ *
300
+ * The default of an `enum` option with `choices` is checked when it would be used, that is, when
301
+ * no value from the command line is used: one that is not one of the choices is reported as an
302
+ * `ArgResolveError` with type 'type' and the code `err:arg:invalid-default`
303
+ * ({@link ArgsValidationErrorKeys}.invalidDefault), and is not used. With a `parse` function of
304
+ * your own, or a `map()` transform, the default is a value that the function returns, which need
305
+ * not be one of the choices, and it is not checked. `choice()` returns the value as is, so its
306
+ * default is checked.
307
+ *
308
+ * For positional arguments, `multiple` ones included, the default is used when no value is left
309
+ * for the argument: when the positional values run out, or when the remaining ones are preserved
310
+ * for later required positional arguments. With `required: true`, the default is not used, and
311
+ * the argument is reported as required instead.
253
312
  *
254
313
  * @example
255
314
  * Default values by type:
@@ -282,7 +341,8 @@ interface ArgSchema {
282
341
  * When `true`, a property like `maxCount` becomes available as `--max-count`.
283
342
  * This allows [CAC](https://github.com/cacjs/cac) user-friendly property names while maintaining CLI conventions.
284
343
  *
285
- * Can be overridden globally with `resolveArgs({ toKebab: true })`.
344
+ * The `toKebab` option of {@link resolveArgs} and `parse()` applies it to all arguments, as in
345
+ * `resolveArgs(args, tokens, { toKebab: true })`.
286
346
  *
287
347
  * Note: Only `true` is allowed (not `false`) to make intent explicit.
288
348
  *
@@ -306,8 +366,19 @@ interface ArgSchema {
306
366
  /**
307
367
  * Names of other options that conflict with this option.
308
368
  *
309
- * When this option is used together with any of the conflicting options,
310
- * an `ArgResolveError` with type 'conflict' will be thrown.
369
+ * When this option is used together with any of the conflicting options, the error is an
370
+ * `ArgResolveError` with type 'conflict' and the code `err:arg:conflict`
371
+ * ({@link ArgsValidationErrorKeys}.conflict). Its `values` has the `displayName` and `name` of
372
+ * the argument whose `conflicts` names the other one, and the `conflictDisplayName` and
373
+ * `conflictName` of the other one. `displayName` and `conflictDisplayName` show an option as
374
+ * it was written, such as `'-p'`, and `name` and `conflictName` are the schema keys. When both
375
+ * name each other, `name` is the one that comes first in the schema.
376
+ *
377
+ * A positional argument can be on either side, such as `[file]` and `--stdin`. It conflicts when
378
+ * it is given, and it is shown by its name, such as `'file'` (in kebab-case with `toKebab`), as
379
+ * in its other errors. The message starts with the kind of the argument given as `name`:
380
+ * `Positional argument 'file' conflicts with '--stdin'` or
381
+ * `Optional argument '--stdin' conflicts with 'file'`.
311
382
  *
312
383
  * Conflicts only need to be defined on one side - if option A defines a conflict
313
384
  * with option B, the conflict is automatically detected when both are used,
@@ -345,7 +416,7 @@ interface ArgSchema {
345
416
  * // No conflicts defined, but still cannot use with --summer
346
417
  * }
347
418
  * }
348
- * // Usage: --summer --autumn will throw error
419
+ * // Usage: --summer --autumn reports an error
349
420
  * // Error: "Optional argument '--summer' conflicts with '--autumn'"
350
421
  * ```
351
422
  *
@@ -397,6 +468,10 @@ interface ArgSchema {
397
468
  * Particularly useful for `type: 'custom'` arguments where the type
398
469
  * name would otherwise be unhelpful.
399
470
  *
471
+ * For a `custom` option that is given without a value, it is also the `expected` in the `values`
472
+ * of the `err:arg:missing-value` error ({@link ArgsValidationErrorKeys}.missingValue), which is
473
+ * `'custom'` without it.
474
+ *
400
475
  * @example
401
476
  * Metavar usage:
402
477
  * ```ts
@@ -413,13 +488,37 @@ interface ArgSchema {
413
488
  */
414
489
  metavar?: string;
415
490
  /**
416
- * Custom parsing function for `type: 'custom'` arguments.
491
+ * Custom parsing function, used in place of the parsing of the argument's `type`.
417
492
  *
418
- * Required when `type: 'custom'`. Receives the raw string value and must
419
- * return the parsed result. Should throw an Error (or subclass) if parsing fails.
493
+ * Required when `type: 'custom'`: if it is missing or not a function, {@link resolveArgs} and
494
+ * `parse()` throw a `TypeError`, whether or not the argument is given. The function receives the
495
+ * raw string value and must return the parsed result. It should throw an Error (or subclass) if
496
+ * parsing fails.
420
497
  *
421
498
  * The function's return type becomes the resolved argument type.
422
499
  *
500
+ * `parse` is called synchronously, and what it returns becomes the value, even `undefined` or
501
+ * `null`, so throw to reject a value. An `async` function returns a promise, and the promise
502
+ * becomes the value as is: it is not awaited, and its rejection is not reported as an error.
503
+ * Await the value, or each of its elements with `multiple` (for example with `Promise.all()`),
504
+ * and handle the rejection yourself: a rejection that nothing handles is an unhandled rejection,
505
+ * which ends a Node.js process by default.
506
+ *
507
+ * A `boolean` option calls `parse` with `'true'`, or `'false'` for the negated form. Other
508
+ * options call it only with a value from the command line: when the option is given without a
509
+ * value, `parse` is not called and the missing value is reported as `err:arg:missing-value`
510
+ * ({@link ArgsValidationErrorKeys}.missingValue). When that error may suggest the argument after
511
+ * the option as its value, such as `--port=-5` for `--port -5`, a `parse` function of your own
512
+ * is not called to check it either: only the `parse` functions of `string()`, `number()`,
513
+ * `integer()`, `float()` and `choice()`, which have no side effects, are called for that.
514
+ * An explicit empty value, such as `--name=` or `-n=`, is passed as `''` unless
515
+ * `required: true` is set.
516
+ *
517
+ * An `enum` option with `choices` calls it only with one of them. Any other value, an explicit
518
+ * empty one included, is reported as `err:arg:invalid-choice`
519
+ * ({@link ArgsValidationErrorKeys}.invalidChoice), except that a required option reports an
520
+ * explicit empty value as required.
521
+ *
423
522
  * @param value - Raw string value from command line
424
523
  * @returns Parsed value of any type
425
524
  * @throws {Error} Error or subclass when value is invalid
@@ -530,8 +629,8 @@ type CommonArgType = {
530
629
  };
531
630
  declare const COMMON_ARGS: CommonArgType;
532
631
  declare const COMMAND_BUILTIN_RESOURCE_KEYS: readonly ["USAGE", "COMMAND", "SUBCOMMAND", "COMMANDS", "ARGUMENTS", "OPTIONS", "EXAMPLES", "FORMORE", "NEGATABLE", "DEFAULT", "CHOICES"];
533
- declare const ARG_ERROR_RESOURCE_KEYS: readonly ["err:arg:required-option", "err:arg:required-positional", "err:arg:invalid-type", "err:arg:invalid-choice", "err:arg:custom-parse", "err:arg:unknown-option"];
534
- declare const COMMAND_ERROR_RESOURCE_KEYS: readonly ["err:cmd:not-found"];
632
+ declare const ARG_ERROR_RESOURCE_KEYS: readonly ["err:arg:required-option", "err:arg:required-positional", "err:arg:invalid-type", "err:arg:invalid-choice", "err:arg:custom-parse", "err:arg:unknown-option", "err:arg:unexpected-value", "err:arg:missing-value", "err:arg:conflict", "err:arg:invalid-default"];
633
+ declare const COMMAND_ERROR_RESOURCE_KEYS: readonly ["err:cmd:not-found", "err:cmd:ambiguous", "err:cmd:inconsistent-options", "err:cmd:lazy-schema-mismatch"];
535
634
  declare const SUGGESTION_ERROR_RESOURCE_KEYS: readonly ["err:suggestion:did-you-mean"];
536
635
  //#endregion
537
636
  //#region ../gunshi/src/index.d.ts
package/lib/index.js CHANGED
@@ -39,7 +39,14 @@ var en_US_default = {
39
39
  "err:arg:invalid-choice": "Invalid value for {$displayName}: expected one of {$choices}",
40
40
  "err:arg:custom-parse": "Invalid value for {$displayName}: {$reason}",
41
41
  "err:arg:unknown-option": "Unknown option: {$rawName}",
42
+ "err:arg:unexpected-value": "Optional argument {$displayName} does not take a value",
43
+ "err:arg:missing-value": "Optional argument {$displayName} requires a value",
44
+ "err:arg:conflict": "{$displayName} conflicts with {$conflictDisplayName}",
45
+ "err:arg:invalid-default": "Invalid default for {$displayName}: {$actual} is not one of {$choices}",
42
46
  "err:cmd:not-found": "Command not found: {$commandName}",
47
+ "err:cmd:ambiguous": "The command is ambiguous. Move options after the command name: {$candidatePaths}",
48
+ "err:cmd:inconsistent-options": "The options do not identify a consistent command. Move options after the command name.",
49
+ "err:cmd:lazy-schema-mismatch": "The lazy command definition does not match its loaded options. Declare leading options in the lazy definition or move options after the command name: {$commandPath}",
43
50
  "err:suggestion:did-you-mean": "Did you mean {$name}?"
44
51
  };
45
52
  //#endregion
@@ -376,6 +383,7 @@ function i18n(options = {}) {
376
383
  fallbackLocale: DEFAULT_LOCALE
377
384
  });
378
385
  const localeBuiltinResources = /* @__PURE__ */ new Map();
386
+ const loadedCommandResources = /* @__PURE__ */ new Map();
379
387
  const builtinGlobalOptions = Object.keys(COMMON_ARGS);
380
388
  const globalOptionResources = /* @__PURE__ */ new Map();
381
389
  return plugin({
@@ -445,7 +453,17 @@ function i18n(options = {}) {
445
453
  const globalOptionKey = resolveArgKey(globalOption, ctx.name);
446
454
  resource[globalOptionKey] = optionResource[localeStr] || optionResource["en-US"] || Object.values(optionResource)[0] || "";
447
455
  }
448
- adapter.setResource(localeStr, Object.assign(Object.create(null), adapter.getResource(localeStr), resource));
456
+ /**
457
+ * NOTE(kazupon): merge into the resource this function handed to the adapter before,
458
+ * when the adapter still holds it. Copying it on every call makes loading N commands
459
+ * cost O(N^2), which shows up when a plugin loads the resources of a whole command tree.
460
+ * Anything else the adapter returns is left alone: a custom adapter may return a copy,
461
+ * the resource of another locale as a fallback, or freeze what it is given.
462
+ */
463
+ const currentResource = adapter.getResource(localeStr);
464
+ const mergedResource = currentResource && loadedCommandResources.get(localeStr) === currentResource && Object.isExtensible(currentResource) ? Object.assign(currentResource, resource) : Object.assign(Object.create(null), currentResource, resource);
465
+ loadedCommandResources.set(localeStr, mergedResource);
466
+ adapter.setResource(localeStr, mergedResource);
449
467
  return loaded;
450
468
  }
451
469
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gunshi/plugin-i18n",
3
3
  "description": "internationalization plugin for gunshi",
4
- "version": "0.37.3",
4
+ "version": "1.0.0-beta.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -53,10 +53,10 @@
53
53
  }
54
54
  },
55
55
  "dependencies": {
56
- "@gunshi/plugin": "0.37.3"
56
+ "@gunshi/plugin": "1.0.0-beta.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@gunshi/plugin-global": "0.37.3"
59
+ "@gunshi/plugin-global": "1.0.0-beta.0"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@gunshi/plugin-global": {
@@ -71,12 +71,12 @@
71
71
  "messageformat": "4.0.0",
72
72
  "publint": "^0.3.20",
73
73
  "tsdown": "0.21.0",
74
- "@gunshi/plugin-global": "0.37.3",
75
- "@gunshi/bone": "0.37.3",
76
- "@gunshi/plugin-renderer": "0.37.3",
77
- "@gunshi/resources": "0.37.3",
78
- "@gunshi/shared": "0.37.3",
79
- "gunshi": "0.37.3"
74
+ "@gunshi/bone": "1.0.0-beta.0",
75
+ "@gunshi/plugin-global": "1.0.0-beta.0",
76
+ "@gunshi/plugin-renderer": "1.0.0-beta.0",
77
+ "@gunshi/shared": "1.0.0-beta.0",
78
+ "@gunshi/resources": "1.0.0-beta.0",
79
+ "gunshi": "1.0.0-beta.0"
80
80
  },
81
81
  "scripts": {
82
82
  "build": "tsdown",