@optique/discover 1.1.0 → 1.2.0-dev.2169

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/README.md CHANGED
@@ -14,7 +14,8 @@ handler.
14
14
  > *@optique/discover* reads command files and imports them dynamically at
15
15
  > runtime. It is a poor fit for CLIs that rely on aggressive tree shaking,
16
16
  > static bundling, or single-file executable packaging. In those cases, use
17
- > manually imported commands with `runProgram({ commands })`.
17
+ > `commandsFromModules()` with a static module map, or manually imported
18
+ > commands with `runProgram({ commands })`.
18
19
 
19
20
 
20
21
  Installation
@@ -79,32 +80,23 @@ admin --help
79
80
  admin completion bash
80
81
  ~~~~
81
82
 
82
- For bundlers and single-file packagers, import commands manually and declare
83
- their paths in the command definitions:
83
+ For bundlers and single-file packagers, turn a static module map into command
84
+ entries:
84
85
 
85
86
  ~~~~ typescript
86
87
  // cli.ts
87
- import { defineCommand, runProgram } from "@optique/discover";
88
- import { object } from "@optique/core/constructs";
88
+ import { commandsFromModules, runProgram } from "@optique/discover";
89
89
  import { message } from "@optique/core/message";
90
- import { option } from "@optique/core/primitives";
91
- import { string } from "@optique/core/valueparser";
92
90
 
93
- const addUser = defineCommand({
94
- path: ["user", "add"],
95
- parser: object({
96
- name: option("--name", string()),
97
- }),
98
- metadata: {
99
- brief: message`Add a user.`,
100
- },
101
- handler(value) {
102
- console.log(`Adding ${value.name}.`);
103
- },
91
+ const modules = import.meta.glob("./commands/**/*.ts", {
92
+ eager: true,
104
93
  });
105
94
 
106
95
  await runProgram({
107
- commands: [addUser],
96
+ commands: commandsFromModules(modules, {
97
+ base: "./commands",
98
+ extensions: [".ts"],
99
+ }),
108
100
  metadata: {
109
101
  name: "admin",
110
102
  version: "1.0.0",
@@ -113,10 +105,20 @@ await runProgram({
113
105
  });
114
106
  ~~~~
115
107
 
108
+ `commandsFromModules()` preserves the file-based command layout while making
109
+ the module list visible to bundlers. For smaller registries, you can also
110
+ import commands manually, declare `path` in each `defineCommand()` call, and
111
+ pass those commands to `runProgram({ commands })`.
112
+
116
113
  By default, Deno and Bun discover `.ts`, `.mts`, `.js`, and `.mjs` files.
117
114
  Node.js discovers `.js`, `.mjs`, and `.cjs` files, plus `.ts`, `.mts`, and
118
115
  `.cts` when it reports native TypeScript support or runs with a recognized
119
116
  TypeScript loader. TypeScript declaration files such as `.d.ts` are ignored.
117
+ Entry files named `index` map to their containing command path, so
118
+ `commands/index.ts` defines the root command and `commands/user/index.ts`
119
+ defines `user`. Use `entryFileName` to choose another entry name or disable
120
+ this rule. `commandsFromModules()` applies the same path rules to module map
121
+ keys after stripping its `base` option.
120
122
 
121
123
  For more resources, see the [docs] and the [*examples/*](/examples/)
122
124
  directory.
@@ -21,10 +21,10 @@ function isCommand(value) {
21
21
  return value != null && typeof value === "object" && value[commandBrand] === true && (value.path == null || isCommandPath(value.path)) && isParser(value.parser) && typeof value.handler === "function";
22
22
  }
23
23
  function validateCommandPath(path) {
24
- if (!isCommandPath(path)) throw new TypeError("Command path must be a non-empty array of non-empty strings.");
24
+ if (!isCommandPath(path)) throw new TypeError("Command path must be an array of non-empty strings.");
25
25
  }
26
26
  function isCommandPath(path) {
27
- return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string" && segment.length > 0);
27
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
28
28
  }
29
29
  function isParser(value) {
30
30
  return value != null && typeof value === "object" && typeof value.parse === "function" && typeof value.complete === "function" && typeof value.suggest === "function" && typeof value.getDocFragments === "function" && (value.mode === "sync" || value.mode === "async");
@@ -20,10 +20,10 @@ function isCommand(value) {
20
20
  return value != null && typeof value === "object" && value[commandBrand] === true && (value.path == null || isCommandPath(value.path)) && isParser(value.parser) && typeof value.handler === "function";
21
21
  }
22
22
  function validateCommandPath(path) {
23
- if (!isCommandPath(path)) throw new TypeError("Command path must be a non-empty array of non-empty strings.");
23
+ if (!isCommandPath(path)) throw new TypeError("Command path must be an array of non-empty strings.");
24
24
  }
25
25
  function isCommandPath(path) {
26
- return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string" && segment.length > 0);
26
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
27
27
  }
28
28
  function isParser(value) {
29
29
  return value != null && typeof value === "object" && typeof value.parse === "function" && typeof value.complete === "function" && typeof value.suggest === "function" && typeof value.getDocFragments === "function" && (value.mode === "sync" || value.mode === "async");
@@ -14,11 +14,13 @@ declare const commandBrand: unique symbol;
14
14
  */
15
15
  type CommandMetadata = CommandOptions;
16
16
  /**
17
- * Non-empty command path used by static command registration.
17
+ * Command path used by static command registration.
18
+ *
19
+ * An empty path represents the root command.
18
20
  *
19
21
  * @since 1.1.0
20
22
  */
21
- type CommandPath = readonly [string, ...string[]];
23
+ type CommandPath = readonly string[];
22
24
  /**
23
25
  * Input accepted by {@link defineCommand}.
24
26
  *
@@ -29,6 +31,7 @@ type CommandPath = readonly [string, ...string[]];
29
31
  interface CommandDefinition<M extends Mode, T> {
30
32
  /**
31
33
  * Command path used when commands are passed directly to `runProgram()`.
34
+ * Use an empty path (`[]`) to register the root command.
32
35
  *
33
36
  * File-based discovery derives the command path from the file name and uses
34
37
  * this field only to validate that the declared path matches.
@@ -14,11 +14,13 @@ declare const commandBrand: unique symbol;
14
14
  */
15
15
  type CommandMetadata = CommandOptions;
16
16
  /**
17
- * Non-empty command path used by static command registration.
17
+ * Command path used by static command registration.
18
+ *
19
+ * An empty path represents the root command.
18
20
  *
19
21
  * @since 1.1.0
20
22
  */
21
- type CommandPath = readonly [string, ...string[]];
23
+ type CommandPath = readonly string[];
22
24
  /**
23
25
  * Input accepted by {@link defineCommand}.
24
26
  *
@@ -29,6 +31,7 @@ type CommandPath = readonly [string, ...string[]];
29
31
  interface CommandDefinition<M extends Mode, T> {
30
32
  /**
31
33
  * Command path used when commands are passed directly to `runProgram()`.
34
+ * Use an empty path (`[]`) to register the root command.
32
35
  *
33
36
  * File-based discovery derives the command path from the file name and uses
34
37
  * this field only to validate that the declared path matches.
package/dist/command.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_command = require('./command-DZA06E08.cjs');
1
+ const require_command = require('./command-B0lV6NBO.cjs');
2
2
 
3
3
  exports.defineCommand = require_command.defineCommand;
4
4
  exports.isCommand = require_command.isCommand;
@@ -1,2 +1,2 @@
1
- import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-2HtR3-TV.cjs";
1
+ import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DSHBTa5c.cjs";
2
2
  export { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand };
package/dist/command.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-BAVhIzfI.js";
1
+ import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DyiVIMUh.js";
2
2
  export { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand };
package/dist/command.js CHANGED
@@ -1,3 +1,3 @@
1
- import { defineCommand, isCommand } from "./command-y_A3hG0g.js";
1
+ import { defineCommand, isCommand } from "./command-DO5zgkvS.js";
2
2
 
3
3
  export { defineCommand, isCommand };