yargs-file-commands 0.0.15 → 0.0.19

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/CHANGELOG.md CHANGED
@@ -1,25 +1,29 @@
1
1
  # yargs-file-commands
2
2
 
3
+ ## 0.0.18
4
+
5
+ ### Patch Changes
6
+
7
+ - Improved debugging logging
8
+ - fix windows bug
9
+
10
+ ## 0.0.17
11
+
12
+ ### Patch Changes
13
+
14
+ - Throw if a command exports unrecognized names, catches a lot of bugs.
15
+
3
16
  ## 0.0.15
4
17
 
5
18
  ### Patch Changes
6
19
 
7
20
  - Throw if a command exports unrecognized names, catches a lot of bugs.
8
- - More explicit exceptions when bad parameters are passed in
9
- - Validate that provided command directories are aboslute
10
- - More robust parameter checking and logging
11
- - Improved debugging logging
12
- - More robust debug messages
13
21
 
14
22
  ## 0.0.14
15
23
 
16
24
  ### Patch Changes
17
25
 
18
26
  - More explicit exceptions when bad parameters are passed in
19
- - Validate that provided command directories are aboslute
20
- - More robust parameter checking and logging
21
- - Improved debugging logging
22
- - More robust debug messages
23
27
 
24
28
  ## 0.0.13
25
29
 
package/README.md CHANGED
@@ -39,13 +39,14 @@ You can use any combination of file names and directories. We support either [Ne
39
39
  │ ├── migration
40
40
  │ │ └── command.ts // the "db migration" command
41
41
  │ └── health.ts // the "db health" command
42
+ ├── $default.ts // the default command
42
43
  └── studio.start.ts // the "studio start" command
43
44
  ```
44
45
 
45
- Inside each route handler file, you make the default export the route handler. Here is a simple example:
46
+ Inside each route handler file, you define your command configuration. The command name defaults to the filename, but you can explicitly specify it using the `command` export to support positional arguments. Here are some examples:
46
47
 
47
48
  ```ts
48
- // commands/studio.start.ts
49
+ // commands/studio.start.ts - Basic command using filename as command name
49
50
 
50
51
  import type { ArgumentsCamelCase, Argv } from 'yargs';
51
52
  import type { BaseOptions } from '../options.js';
@@ -54,6 +55,8 @@ export interface Options extends BaseOptions {
54
55
  port?: number;
55
56
  }
56
57
 
58
+ export const command = 'start'; // this is optional, it will use the filename if this isn't specified
59
+
57
60
  export const describe = 'Studio web interface';
58
61
 
59
62
  export const builder = (args: Argv): Argv<Options> => {
@@ -71,6 +74,35 @@ export const handler = async (args: ArgumentsCamelCase<Options>) => {
71
74
  };
72
75
  ```
73
76
 
77
+ ```ts
78
+ // Command with positional arguments
79
+
80
+ export const command = 'create [name]';
81
+ export const describe = 'Create a new migration';
82
+
83
+ export const builder = (args: Argv): Argv<Options> => {
84
+ return args.positional('name', {
85
+ describe: 'Name of the migration',
86
+ type: 'string',
87
+ demandOption: true
88
+ });
89
+ };
90
+
91
+ export const handler = async (args: ArgumentsCamelCase<Options>) => {
92
+ // Implementation
93
+ };
94
+ ```
95
+
96
+ ```ts
97
+ // Must be named $default.ts - Default command (runs when no command is specified)
98
+
99
+ export const describe = 'Default command';
100
+
101
+ export const handler = async (args: ArgumentsCamelCase<Options>) => {
102
+ console.log('Running default command');
103
+ };
104
+ ```
105
+
74
106
  The above will result in these commands being registered:
75
107
 
76
108
  ```
@@ -10,4 +10,6 @@ export interface Command {
10
10
  segments: string[];
11
11
  /** The Yargs command module implementation */
12
12
  commandModule: CommandModule;
13
+ /** Whether this is the default command */
14
+ isDefault?: boolean;
13
15
  }
@@ -38,7 +38,7 @@ function insertIntoTree(treeNodes, command, depth) {
38
38
  });
39
39
  }
40
40
  else if (currentSegment.type === 'internal') {
41
- throw new Error(`Conflict: ${currentSegmentName} is both a directory and a command`);
41
+ throw new Error(`Conflict: ${currentSegmentName} is both a directory and a command ${JSON.stringify(currentSegment)},${JSON.stringify(command)}`);
42
42
  }
43
43
  return;
44
44
  }
@@ -52,7 +52,7 @@ function insertIntoTree(treeNodes, command, depth) {
52
52
  treeNodes.push(currentSegment);
53
53
  }
54
54
  else if (currentSegment.type === 'leaf') {
55
- throw new Error(`Conflict: ${currentSegmentName} is both a directory and a command`);
55
+ throw new Error(`Conflict: ${currentSegmentName} is both a directory and a command ${JSON.stringify(currentSegment)}, ${JSON.stringify(command)}`);
56
56
  }
57
57
  // Recurse into children
58
58
  insertIntoTree(currentSegment.children, command, depth + 1);
@@ -1,43 +1,36 @@
1
- import assert from 'node:assert/strict';
2
- import path from 'node:path';
3
- import { describe, it } from 'node:test';
4
- import { fileURLToPath } from 'node:url';
5
- import yargs from 'yargs';
6
- import { hideBin } from 'yargs/helpers';
7
- import { fileCommands } from '../lib/fileCommands.js';
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- describe('fileCommands', async () => {
10
- await it('should load commands from directory structure', async () => {
11
- const commandsDir = path.join(__dirname, 'fixtures', 'commands');
12
- const commands = await fileCommands({
13
- commandDirs: [commandsDir],
14
- extensions: ['.js'],
15
- logLevel: 'debug'
16
- });
17
- assert.equal(commands.length, 1, 'Should have one root command');
18
- const rootCommand = commands[0];
19
- assert(rootCommand, 'Root command should exist');
20
- assert.equal(rootCommand.command, 'db', 'Root command should be "db"');
21
- // Create a new yargs instance
22
- const yargsInstance = yargs(hideBin(process.argv));
23
- if (typeof rootCommand.builder === 'function') {
24
- rootCommand.builder(yargsInstance);
25
- }
26
- // Check that the command has subcommands by checking its description
27
- const description = rootCommand.describe;
28
- assert(typeof description === 'string' && description.includes('db'), 'Command should have correct description');
1
+ import path from 'path';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import { fileCommands } from './fileCommands.js';
5
+ // get __dirname in ESM style
6
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
7
+ test('should load commands from directory structure', async () => {
8
+ const commands = await fileCommands({
9
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
10
+ logLevel: 'debug'
29
11
  });
30
- await it('should respect ignore patterns', async () => {
31
- const commandsDir = path.join(__dirname, 'fixtures', 'commands');
32
- const commands = await fileCommands({
33
- commandDirs: [commandsDir],
34
- extensions: ['.js'],
35
- ignorePatterns: [/health/],
36
- logLevel: 'debug'
37
- });
38
- assert.equal(commands.length, 1, 'Should have one root command');
39
- const rootCommand = commands[0];
40
- assert(rootCommand, 'Root command should exist');
41
- assert.equal(rootCommand.command, 'db', 'Root command should be "db"');
12
+ assert.ok(commands.length > 0);
13
+ });
14
+ test('should respect ignore patterns', async () => {
15
+ const commands = await fileCommands({
16
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
17
+ ignorePatterns: [/health/, /.d.ts/],
18
+ logLevel: 'debug'
19
+ });
20
+ assert.ok(commands.length > 0);
21
+ });
22
+ test('should handle explicit commands and default command', async () => {
23
+ const commands = await fileCommands({
24
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
25
+ logLevel: 'debug'
42
26
  });
27
+ console.log('commands', JSON.stringify(commands.map((c) => c.command), null, 2));
28
+ // Find the explicit command
29
+ const explicitCommand = commands.find((cmd) => cmd.command?.toString().includes('create [name]'));
30
+ assert.ok(explicitCommand, 'Should find explicit command');
31
+ assert.equal(explicitCommand?.describe, 'Create something with a name');
32
+ // Find the default command
33
+ const defaultCommand = commands.find((cmd) => cmd.command === '$0');
34
+ assert.ok(defaultCommand, 'Should find default command');
35
+ assert.equal(defaultCommand?.describe, 'Default command');
43
36
  });
@@ -0,0 +1,2 @@
1
+ export declare const describe = "Default command";
2
+ export declare const handler: () => Promise<void>;
@@ -0,0 +1,4 @@
1
+ export const describe = 'Default command';
2
+ export const handler = async () => {
3
+ // Default command implementation
4
+ };
@@ -0,0 +1,3 @@
1
+ export declare const command = "create [name]";
2
+ export declare const describe = "Create something with a name";
3
+ export declare const handler: () => Promise<void>;
@@ -0,0 +1,5 @@
1
+ export const command = 'create [name]';
2
+ export const describe = 'Create something with a name';
3
+ export const handler = async () => {
4
+ // Create command implementation
5
+ };
@@ -16,10 +16,13 @@ export const importCommandFromFile = async (filePath, name, options) => {
16
16
  if (fs.existsSync(filePath) === false) {
17
17
  throw new Error(`Can not import command from non-existence file path: ${filePath}`);
18
18
  }
19
- const handlerModule = (await import(filePath));
19
+ const url = 'file://' + filePath;
20
+ const handlerModule = (await import(url));
20
21
  const { logLevel = 'info' } = options;
22
+ // Check if this is the default command
23
+ const isDefault = name === '$default';
21
24
  const command = {
22
- command: name,
25
+ command: handlerModule.command ?? (isDefault ? '$0' : name),
23
26
  describe: handlerModule.describe,
24
27
  alias: handlerModule.alias,
25
28
  builder: handlerModule.builder,
@@ -30,6 +33,7 @@ export const importCommandFromFile = async (filePath, name, options) => {
30
33
  })
31
34
  };
32
35
  const supportedNames = [
36
+ 'command',
33
37
  'describe',
34
38
  'alias',
35
39
  'builder',
@@ -1,21 +1,42 @@
1
- import assert from 'node:assert/strict';
2
- import path from 'node:path';
3
- import { describe, it } from 'node:test';
4
- import { fileURLToPath } from 'node:url';
5
- import { importCommandFromFile } from '../lib/importCommand.js';
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- describe('importCommandFromFile', async () => {
8
- await it('should import command module correctly', async () => {
9
- const commandPath = path.join(__dirname, 'fixtures', 'commands', 'db', 'health.js');
10
- const command = await importCommandFromFile(commandPath, 'health', {});
11
- assert(command.describe, 'Should have describe property');
12
- assert(typeof command.builder === 'function', 'Should have builder function');
13
- assert(typeof command.handler === 'function', 'Should have handler function');
1
+ import path from 'path';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import { importCommandFromFile } from './importCommand.js';
5
+ // get __dirname in ESM style
6
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
7
+ test('should import command module correctly', async () => {
8
+ const filePath = path.join(__dirname, 'fixtures', 'commands', 'db', 'health.js');
9
+ const command = await importCommandFromFile(filePath, 'health', {
10
+ logLevel: 'info'
14
11
  });
15
- await it('should handle non-existent files', async () => {
16
- const nonExistentPath = path.join(__dirname, 'fixtures', 'commands', 'non-existent.ts');
17
- await assert.rejects(async () => {
18
- await importCommandFromFile(nonExistentPath, 'non-existent', {});
19
- }, Error, 'Should throw error for non-existent file');
12
+ assert.equal(command.describe, 'Database health check');
13
+ });
14
+ test('should handle non-existent files', async () => {
15
+ const filePath = path.join(__dirname, 'fixtures', 'commands', 'non-existent.js');
16
+ try {
17
+ await importCommandFromFile(filePath, 'non-existent', {
18
+ logLevel: 'info'
19
+ });
20
+ assert.fail('Should have thrown an error');
21
+ }
22
+ catch (error) {
23
+ assert.ok(error instanceof Error);
24
+ assert.ok(error.message.includes('Can not import command from non-existence'));
25
+ }
26
+ });
27
+ test('should handle explicit command names', async () => {
28
+ const filePath = path.join(__dirname, 'fixtures', 'commands', 'create.js');
29
+ const command = await importCommandFromFile(filePath, 'create', {
30
+ logLevel: 'info'
31
+ });
32
+ assert.equal(command.command, 'create [name]');
33
+ assert.equal(command.describe, 'Create something with a name');
34
+ });
35
+ test('should handle default commands', async () => {
36
+ const filePath = path.join(__dirname, 'fixtures', 'commands', '$default.js');
37
+ const command = await importCommandFromFile(filePath, '$default', {
38
+ logLevel: 'info'
20
39
  });
40
+ assert.equal(command.command, '$0');
41
+ assert.equal(command.describe, 'Default command');
21
42
  });
@@ -12,7 +12,7 @@ describe('scanDirectory', async () => {
12
12
  extensions: ['.js'],
13
13
  logLevel: 'debug'
14
14
  });
15
- assert.equal(files.length, 2, `Should find two command files, instead found: ${files.join(', ')}`);
15
+ assert.equal(files.length, 4, `Should find two command files, instead found: ${files.join(', ')}`);
16
16
  assert(files.some((f) => f.includes('health.js')), `Should find health.js, instead found: ${files.join(', ')}`);
17
17
  assert(files.some((f) => f.includes('command.js')), `Should find command.js, instead found: ${files.join(', ')}`);
18
18
  });
@@ -21,10 +21,10 @@ describe('scanDirectory', async () => {
21
21
  console.log('Scan Directory: ', commandsDir);
22
22
  const files = await scanDirectory(commandsDir, commandsDir, {
23
23
  extensions: ['.js'],
24
- ignorePatterns: [/health/],
24
+ ignorePatterns: [/health/, /.d.ts/],
25
25
  logLevel: 'debug'
26
26
  });
27
- assert.equal(files.length, 1, `Should find one command file, instead found: ${files.join(', ')}`);
27
+ assert.equal(files.length, 3, `Should find one command file, instead found: ${files.join(', ')}`);
28
28
  assert(files.some((f) => f.includes('command.js')), `Should find command.js, instead found: ${files.join(', ')}`);
29
29
  assert(!files.some((f) => f.includes('health.js')), `Should not find health.js, instead found: ${files.join(', ')}`);
30
30
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yargs-file-commands",
3
3
  "description": "A yargs helper function that lets you define your commands structure via directory and file naming conventions.",
4
- "version": "0.0.15",
4
+ "version": "0.0.19",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -11,4 +11,6 @@ export interface Command {
11
11
  segments: string[];
12
12
  /** The Yargs command module implementation */
13
13
  commandModule: CommandModule;
14
+ /** Whether this is the default command */
15
+ isDefault?: boolean;
14
16
  }
@@ -75,7 +75,9 @@ function insertIntoTree(
75
75
  });
76
76
  } else if (currentSegment.type === 'internal') {
77
77
  throw new Error(
78
- `Conflict: ${currentSegmentName} is both a directory and a command`
78
+ `Conflict: ${currentSegmentName} is both a directory and a command ${JSON.stringify(
79
+ currentSegment
80
+ )},${JSON.stringify(command)}`
79
81
  );
80
82
  }
81
83
  return;
@@ -91,7 +93,9 @@ function insertIntoTree(
91
93
  treeNodes.push(currentSegment);
92
94
  } else if (currentSegment.type === 'leaf') {
93
95
  throw new Error(
94
- `Conflict: ${currentSegmentName} is both a directory and a command`
96
+ `Conflict: ${currentSegmentName} is both a directory and a command ${JSON.stringify(
97
+ currentSegment
98
+ )}, ${JSON.stringify(command)}`
95
99
  );
96
100
  }
97
101
 
@@ -1,57 +1,55 @@
1
- import assert from 'node:assert/strict';
2
- import path from 'node:path';
3
- import { describe, it } from 'node:test';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- import type { CommandModule } from 'yargs';
7
- import yargs from 'yargs';
8
- import { hideBin } from 'yargs/helpers';
9
-
10
- import { fileCommands } from '../lib/fileCommands.js';
11
-
12
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
-
14
- describe('fileCommands', async () => {
15
- await it('should load commands from directory structure', async () => {
16
- const commandsDir = path.join(__dirname, 'fixtures', 'commands');
17
- const commands = await fileCommands({
18
- commandDirs: [commandsDir],
19
- extensions: ['.js'],
20
- logLevel: 'debug'
21
- });
22
-
23
- assert.equal(commands.length, 1, 'Should have one root command');
24
- const rootCommand = commands[0] as CommandModule;
25
- assert(rootCommand, 'Root command should exist');
26
- assert.equal(rootCommand.command, 'db', 'Root command should be "db"');
27
-
28
- // Create a new yargs instance
29
- const yargsInstance = yargs(hideBin(process.argv));
30
-
31
- if (typeof rootCommand.builder === 'function') {
32
- rootCommand.builder(yargsInstance);
33
- }
34
-
35
- // Check that the command has subcommands by checking its description
36
- const description = rootCommand.describe;
37
- assert(
38
- typeof description === 'string' && description.includes('db'),
39
- 'Command should have correct description'
40
- );
1
+ import path from 'path';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import type { Command } from './Command.js';
5
+
6
+ import { fileCommands } from './fileCommands.js';
7
+
8
+ // get __dirname in ESM style
9
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
10
+
11
+ test('should load commands from directory structure', async () => {
12
+ const commands = await fileCommands({
13
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
14
+ logLevel: 'debug'
15
+ });
16
+
17
+ assert.ok(commands.length > 0);
18
+ });
19
+
20
+ test('should respect ignore patterns', async () => {
21
+ const commands = await fileCommands({
22
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
23
+ ignorePatterns: [/health/, /.d.ts/],
24
+ logLevel: 'debug'
41
25
  });
42
26
 
43
- await it('should respect ignore patterns', async () => {
44
- const commandsDir = path.join(__dirname, 'fixtures', 'commands');
45
- const commands = await fileCommands({
46
- commandDirs: [commandsDir],
47
- extensions: ['.js'],
48
- ignorePatterns: [/health/],
49
- logLevel: 'debug'
50
- });
51
-
52
- assert.equal(commands.length, 1, 'Should have one root command');
53
- const rootCommand = commands[0] as CommandModule;
54
- assert(rootCommand, 'Root command should exist');
55
- assert.equal(rootCommand.command, 'db', 'Root command should be "db"');
27
+ assert.ok(commands.length > 0);
28
+ });
29
+
30
+ test('should handle explicit commands and default command', async () => {
31
+ const commands = await fileCommands({
32
+ commandDirs: [path.join(__dirname, 'fixtures', 'commands')],
33
+ logLevel: 'debug'
56
34
  });
35
+
36
+ console.log(
37
+ 'commands',
38
+ JSON.stringify(
39
+ commands.map((c) => c.command),
40
+ null,
41
+ 2
42
+ )
43
+ );
44
+ // Find the explicit command
45
+ const explicitCommand = commands.find((cmd) =>
46
+ cmd.command?.toString().includes('create [name]')
47
+ );
48
+ assert.ok(explicitCommand, 'Should find explicit command');
49
+ assert.equal(explicitCommand?.describe, 'Create something with a name');
50
+
51
+ // Find the default command
52
+ const defaultCommand = commands.find((cmd) => cmd.command === '$0');
53
+ assert.ok(defaultCommand, 'Should find default command');
54
+ assert.equal(defaultCommand?.describe, 'Default command');
57
55
  });
@@ -0,0 +1,5 @@
1
+ export const describe = 'Default command';
2
+
3
+ export const handler = async () => {
4
+ // Default command implementation
5
+ };
@@ -0,0 +1,6 @@
1
+ export const command = 'create [name]';
2
+ export const describe = 'Create something with a name';
3
+
4
+ export const handler = async () => {
5
+ // Create command implementation
6
+ };
@@ -1,48 +1,65 @@
1
- import assert from 'node:assert/strict';
2
- import path from 'node:path';
3
- import { describe, it } from 'node:test';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- import { importCommandFromFile } from '../lib/importCommand.js';
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
-
10
- describe('importCommandFromFile', async () => {
11
- await it('should import command module correctly', async () => {
12
- const commandPath = path.join(
13
- __dirname,
14
- 'fixtures',
15
- 'commands',
16
- 'db',
17
- 'health.js'
18
- );
19
- const command = await importCommandFromFile(commandPath, 'health', {});
1
+ import path from 'path';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
20
4
 
21
- assert(command.describe, 'Should have describe property');
22
- assert(
23
- typeof command.builder === 'function',
24
- 'Should have builder function'
25
- );
26
- assert(
27
- typeof command.handler === 'function',
28
- 'Should have handler function'
29
- );
5
+ import { importCommandFromFile } from './importCommand.js';
6
+
7
+ // get __dirname in ESM style
8
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
9
+
10
+ test('should import command module correctly', async () => {
11
+ const filePath = path.join(
12
+ __dirname,
13
+ 'fixtures',
14
+ 'commands',
15
+ 'db',
16
+ 'health.js'
17
+ );
18
+ const command = await importCommandFromFile(filePath, 'health', {
19
+ logLevel: 'info'
30
20
  });
31
21
 
32
- await it('should handle non-existent files', async () => {
33
- const nonExistentPath = path.join(
34
- __dirname,
35
- 'fixtures',
36
- 'commands',
37
- 'non-existent.ts'
38
- );
22
+ assert.equal(command.describe, 'Database health check');
23
+ });
39
24
 
40
- await assert.rejects(
41
- async () => {
42
- await importCommandFromFile(nonExistentPath, 'non-existent', {});
43
- },
44
- Error,
45
- 'Should throw error for non-existent file'
25
+ test('should handle non-existent files', async () => {
26
+ const filePath = path.join(
27
+ __dirname,
28
+ 'fixtures',
29
+ 'commands',
30
+ 'non-existent.js'
31
+ );
32
+ try {
33
+ await importCommandFromFile(filePath, 'non-existent', {
34
+ logLevel: 'info'
35
+ });
36
+ assert.fail('Should have thrown an error');
37
+ } catch (error) {
38
+ assert.ok(error instanceof Error);
39
+ assert.ok(
40
+ (error as Error).message.includes(
41
+ 'Can not import command from non-existence'
42
+ )
46
43
  );
44
+ }
45
+ });
46
+
47
+ test('should handle explicit command names', async () => {
48
+ const filePath = path.join(__dirname, 'fixtures', 'commands', 'create.js');
49
+ const command = await importCommandFromFile(filePath, 'create', {
50
+ logLevel: 'info'
47
51
  });
52
+
53
+ assert.equal(command.command, 'create [name]');
54
+ assert.equal(command.describe, 'Create something with a name');
55
+ });
56
+
57
+ test('should handle default commands', async () => {
58
+ const filePath = path.join(__dirname, 'fixtures', 'commands', '$default.js');
59
+ const command = await importCommandFromFile(filePath, '$default', {
60
+ logLevel: 'info'
61
+ });
62
+
63
+ assert.equal(command.command, '$0');
64
+ assert.equal(command.describe, 'Default command');
48
65
  });
@@ -92,11 +92,16 @@ export const importCommandFromFile = async (
92
92
  );
93
93
  }
94
94
 
95
- const handlerModule = (await import(filePath)) as CommandImportModule;
95
+ const url = 'file://' + filePath;
96
+
97
+ const handlerModule = (await import(url)) as CommandImportModule;
96
98
  const { logLevel = 'info' } = options;
97
99
 
100
+ // Check if this is the default command
101
+ const isDefault = name === '$default';
102
+
98
103
  const command = {
99
- command: name,
104
+ command: handlerModule.command ?? (isDefault ? '$0' : name),
100
105
  describe: handlerModule.describe,
101
106
  alias: handlerModule.alias,
102
107
  builder: handlerModule.builder,
@@ -109,6 +114,7 @@ export const importCommandFromFile = async (
109
114
  } as CommandModule;
110
115
 
111
116
  const supportedNames = [
117
+ 'command',
112
118
  'describe',
113
119
  'alias',
114
120
  'builder',
@@ -18,7 +18,7 @@ describe('scanDirectory', async () => {
18
18
 
19
19
  assert.equal(
20
20
  files.length,
21
- 2,
21
+ 4,
22
22
  `Should find two command files, instead found: ${files.join(', ')}`
23
23
  );
24
24
  assert(
@@ -36,13 +36,13 @@ describe('scanDirectory', async () => {
36
36
  console.log('Scan Directory: ', commandsDir);
37
37
  const files = await scanDirectory(commandsDir, commandsDir, {
38
38
  extensions: ['.js'],
39
- ignorePatterns: [/health/],
39
+ ignorePatterns: [/health/, /.d.ts/],
40
40
  logLevel: 'debug'
41
41
  });
42
42
 
43
43
  assert.equal(
44
44
  files.length,
45
- 1,
45
+ 3,
46
46
  `Should find one command file, instead found: ${files.join(', ')}`
47
47
  );
48
48
  assert(